{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b7b7bbeff6237c20",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-07-11T07:06:05.692112Z",
     "start_time": "2025-07-11T07:05:59.543386Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "\n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n",
    "torch.manual_seed(seed)\n",
    "torch.cuda.manual_seed_all(seed)\n",
    "np.random.seed(seed)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a61058c1",
   "metadata": {},
   "source": [
    "# MosesTokenizer预处理 subword-nmt分词"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "initial_id",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-07-11T07:30:43.221658Z",
     "start_time": "2025-07-11T07:30:27.977023Z"
    },
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# !sh data_multi30k.sh wmt16 wmt16 de en"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "1acf1f44",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-07-11T08:03:52.736243Z",
     "start_time": "2025-07-11T08:03:52.535567Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total sentences: 29000\n",
      "Min length: 2\n",
      "Max length: 47\n",
      "Mean length: 13.03\n",
      "Median length: 12.0\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAJOCAYAAACqS2TfAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAe9xJREFUeJzt3Qd4FNX6+PE3vZBCSwgldKR3FVEUEARFEZWLDcSCol7wCljxegXEK/aOohcFe8GOBUVEbCBKUZp06SVISYD07P95D//Z3yYE2Ozsbiab7+d51sPuzp6dnT075p33lDCXy+USAAAAAADgd+H+rxIAAAAAACiCbgAAAAAAAoSgGwAAAACAACHoBgAAAAAgQAi6AQAAAAAIEIJuAAAAAAAChKAbAAAAAIAAIegGAAAAACBACLoBAAAAAAgQgm4AsGH8+PESFhYWlPfq0aOHuVm+++47897vv/9+UN7/mmuukYYNG4qTHTx4UK6//npJS0szx2bUqFHlvUtwsL/++su0k8ceeywkzhH+Mn36dLPPenzKyjovaQkAOIKgGwBK/KFp3WJjY6VOnTrSt29feeaZZyQrK8sv77N9+3bzh/jSpUvFaZy8b9548MEHzfd48803y+uvvy5XXXXVMbfNy8uTp59+Wjp27ChJSUlStWpVad26tQwfPlz+/PPPgO7nW2+9JU899ZSECr0Y1KZNG3GqL774wrTrUDjOnueoY91C4bM67Rz+888/m+O6f/9+v+4zgMohzOVyucp7JwDAKX+wXXvttXL//fdLo0aNJD8/X3bu3GkyNrNnz5b69evLp59+Ku3atXO/pqCgwNz0jztv/fbbb3LKKafItGnTTPbYWxokqujoaFPqfvXs2VNmzJgh//jHP8r0WX3ZNz0eRUVFEhMTI0512mmnSWRkpPz4448n3LZ///7y5ZdfyhVXXCFdu3Y1n0+D7c8++0wmTpxYpu+mrC644AJZvny5T5lEpwaDe/bsMZ/JiUaOHCmTJ0+Wkn/y6PHX3/qjjz4qt99+e0De25dzxLHoeWjXrl3u+7/++qsJJu+55x5p2bKl+3E9R3mep8qqsLDQ/B70t17WLL2eI/Rcpeep8PBwx5/DvaW9Ie644w7ZuHGj43v8AHCeyPLeAQBwmvPOO09OPvlk9/2xY8fKt99+awKlCy+8UFatWiVxcXHmOQ3w9BZIhw8flvj4eHewXV6ioqLE6Xbv3i2tWrU64XYarGhw/d///tcELJ6ee+45slnwG3+eI84555xi9zWQ16BbH/ccelLSoUOHpEqVKl6/T0REhLn5QgNtf1xgCNY5HACCge7lAOCFs88+W/7zn//Ipk2b5I033jjueE3NqHTr1s10V05ISJDmzZu7AzvNuGgmWWlGxuoGqRkaz266ixYtkrPOOssE29ZrS47p9sxK6TY6jln/sNY/Krds2VJsG83MlJa59azzRPtW2phu/WP+tttuk/T0dJMV08+qGaGSGUWtR7ONH3/8sfl8uq125Z41a5bXwfSwYcOkVq1a5g/69u3by6uvvnrUOFLNQn3++efufT9WJnn9+vWmPOOMM456ToONGjVqFHts27Ztct1115n3t/b9lVdeKbaNtQ/vvfeeCebr1atn9rVXr16ybt26Ysdc91HbkrWfnsc1NzdXxo0bJ02bNjXvpcf2zjvvNI/7ekx1//X4aVdb3U6zgNoF3+o9ofRCg46Bt75Lff+HH37YZC79RXsWnHnmmaadJiYmyvnnny8rVqwoto22M/3d6D5fdNFF5t8pKSkmE61t3dPff/9thhBYwwOuvvpq+f33349qt5rlto6ZdSvppZdekiZNmpjPrr8DvTDjSTOm+rvQ71W3qV27tgwYMOCEvRVKO0fY/T14834rV66UK6+8UqpVq2bOR+qPP/4wx6Nx48ambeo5Q9u1HscTjenWNqpBq/YiOfXUU83rtZ7XXnvthGO6rfOa7pP2ztHzWt26deWRRx45av/1d6HnMG0jqampMnr0aPnqq69sjxM/1jncm2Oix1Sz3Ep/OyXPL9ozSOvX/dXvUi/8vfDCCz7vK4DQQ6YbALykf9xrcPv111/LDTfcUOo2GkDoH6bafVG7OOofYBpw/fTTT+Z57QKqj993331m7LAGIOr0009316F/7Gmm5vLLL5chQ4aYQO94NMDTPwDvuusuE5zqWOHevXubcdllyeZ4s2+eNLDWP47nzp1rAroOHTqYP471j1MNmJ588sli2+sf6x9++KH885//NAGXZugGDhwomzdvPirI9ZSdnW3+aNfjqIGK/tGrXer1D2UNFG+99Vaz7zqGW/9A16BILwQoDdZK06BBA1O++eabJvA+XiZSu/Nqt3UrUNI6NXjUz5yZmXnUZG0PPfSQyfZpkHjgwAETWAwePFh++eUX8/y///1v8/jWrVvdx0gDS6UBrh5TPVb6HejnWrZsmdluzZo1Jkgr6zHVcfoaJOmx0jpbtGhhvh+dgE97UWgPCi27d+9uHr/xxhtNN1wdw6oZwh07dvhl/Ll+PxoU6/haDeb1PTUw0YBwyZIlxS48aHCt23Xp0sVcxPnmm2/k8ccfN0GxXiywjpUOEVi4cKF5TD/XJ598Yt7Dk34ePQZ6MUz34Vhj7HW8r26r37N+Z5dccols2LDB3cNDj6v+vm+55Razr/pb0zr1WPvS3djX34O3Bg0aJM2aNTPzHFgXwXR/9TPpxQMNLvXz6MUGLRcsWHDCruT6G9ShLNr29TjrhSf9HXbu3NlcNDieffv2ybnnnmuO66WXXmran56z2rZta8531kU8DV61zenvWvdRvxs9xwTqHO7NMdF91t/f22+/bX6LNWvWLHZ+0Xasn19/u3oumTlzpvletY2OGDHCL/sOoILTMd0AAJdr2rRp+pep69dffz3mNsnJya6OHTu6748bN868xvLkk0+a+xkZGcesQ+vXbfT9Surevbt5bsqUKaU+pzfL3LlzzbZ169Z1ZWZmuh9/7733zONPP/20+7EGDRq4rr766hPWebx909drPZaPP/7YbPvAAw8U2+4f//iHKywszLVu3Tr3Y7pddHR0scd+//138/izzz7rOp6nnnrKbPfGG2+4H8vLy3N17drVlZCQUOyz6/6df/75rhMpKipyH+tatWq5rrjiCtfkyZNdmzZtOmrbYcOGuWrXru3as2dPsccvv/xy0x4OHz5c7Pto2bKlKzc3172dfg/6+LJly9yP6T56HkvL66+/7goPD3f98MMPxR7X9qB1/PTTT2U+pkOHDjV1ltau9TioiRMnuqpUqeJas2ZNsefvvvtuV0REhGvz5s2u49Fj2bp162M+n5WV5apatarrhhtuKPb4zp07zTH0fFzbmX6G+++/v9i2+rvr3Lmz+/4HH3xgttP2YSksLHSdffbZR7XhESNGFPudWjZu3Gger1Gjhmvv3r3uxz/55BPz+MyZM839ffv2mfuPPvqoq6xKniPs/h48zZgxw7xG217J99M2XZLVVj29/fbbZvvvv//+qHOhHh+LtteS2+3evdsVExPjuu2229yPWb8Dz32yfmuvvfaa+zH9jaSlpbkGDhzofuzxxx832+m5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qampUh7y8vJk0KBBcvPNN5f6/KJFi8y+vfHGG7JixQr597//LWPHjpXnnnvuuPVqwK3bz549Wz777DP5/vvvZfjw4e7nMzMzpU+fPtKgQQPzHo8++qiMHz9eXnrpJQkkgm4Ajrf5wGa5YeYNprRX0WaRG244UgIAADhIp06dTOD94Ycfuh/Tf2vA3bFjx2LbFhUVyaRJk6RRo0YmeG7fvr28//77xbb54osv5KSTTjLP9+zZU/76669iz5fsXr5+/XoZMGCA1KpVSxISEuSUU06Rb775pthrGjZsKA8++KBcd911kpiYaPbNl4B1woQJMnr0aGnbtm2pz2v9mtnu3r27NG7cWIYMGSLXXnttsWNT0qpVq2TWrFkydepU6dKli3Tr1k2effZZcyFj+/btZps333zTBPyvvPKKtG7dWi6//HL517/+JU888YSEbND9wgsvSLt27Uz6X29du3aVL7/8stiVHe1OUaNGDfPFDxw4UHbt2lWsjs2bN8v5559vuibo1ZA77rhDCgoKjhq/oI04JiZGmjZtahoYAAAAADiJBpvTpk1z39fgUIPNkjTgfu2112TKlCkms6sBrAam8+bNM89v2bJFLrnkEunfv78sXbpUrr/+ern77ruP+97aDbtfv34yZ84cWbJkiZx77rnm9RpveXr88cfl5JNPNtv885//NNnq1atXu5/v0aOH37vCqwMHDkj16tXlWObPn28uIui+WXr37i3h4eHyyy+/uLc566yzJDo62r1N3759zf7v27dPAiVSylG9evXkoYcekmbNmonL5ZJXX33VXF3RL1CvPGjj+fzzz2XGjBmSnJwsI0eONI3np59+Mq8vLCw0AXdaWpr8/PPPsmPHDhk6dKhERUWZKzBq48aNZhsdH6FXNrQRaaOrXbu2OcAAAAAAQtuOrB2y4+COYo9Vi60mjao1kpyCHFmZsfKo13Sq3cmUq/eslkP5h4o917BqQ6keV10yDmXIlswtxZ5LjE6UZjWa+bSfGjhrN+pNmzaZ+xr3aKZWk4iW3NxcE+toFlqTlkqzwT/++KO8+OKLJjusyc0mTZqYAFk1b95cli1bJg8//PAx31uz5XqzTJw4UT766CP59NNPTRxm0cBcg2111113yZNPPilz584176E0+62xlj/9/PPP8u6775rY8Fh27tx5VHf5yMhIE6jrc9Y22jvA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AAAAAAgQgm4AjpcZt0y+aVfPlHY0yFwm076pZ0oAAAAgGAi6ATieKyxfcqK3mdKOSFe+1MzZZkoAAAAgGAi6AQAAAAAIEIJuAAAAAAAChKAbAAAAAIAAIegG4HhVcprJaavnmtKO7VWaydjT5poSAAAACAbW6QbgeJFFiVIzq4fterIjE2V5Tfv1AAAAAN4i0w3A8bKjtsmqumNNaUf17G0ydNVYUwIAAADBQNANwPHyonbJ+toPmdKOanm7ZND6h0wJAAAABANBNwAAAAAAAULQDQAAAABAgBB0AwAAAAAQIATdABwvqqCGpGcMM6UdmVE15Ov0YaYEAAAAgoElwwA4XnxeA2m/aartejLiG8iz7e3XAwAAAHiLTDcAxysMy5as2BWmtCO6MFvqZ60wJQAAABAMBN0AHO9g3CqZ16aNKe1IP7hKJs9rY0oAAAAgGAi6AQAAAAAIEIJuAAAAAAAChKAbAAAAAIAAIegG4HyuMAkvijalrWokTPLDo00JAAAABANLhgFwvOTsjtJvca7tejYkd5RL+tmvBwAAAPAWmW4AAAAAAAKEoBuA42XFrpLvW3YypR31slbJU993MiUAAAAQ8kH3pEmT5JRTTpHExERJTU2Viy66SFavXl1sm5ycHBkxYoTUqFFDEhISZODAgbJr165i22zevFnOP/98iY+PN/XccccdUlBQUGyb7777Tjp16iQxMTHStGlTmT59elA+IwD7isKzJbPKElPaEVOULU0yl5gSAAAACPmge968eSagXrBggcyePVvy8/OlT58+cujQIfc2o0ePlpkzZ8qMGTPM9tu3b5dLLrnE/XxhYaEJuPPy8uTnn3+WV1991QTU9913n3ubjRs3mm169uwpS5culVGjRsn1118vX331VdA/MwAAAACg8ijXidRmzZpV7L4Gy5qpXrRokZx11lly4MABefnll+Wtt96Ss88+22wzbdo0admypQnUTzvtNPn6669l5cqV8s0330itWrWkQ4cOMnHiRLnrrrtk/PjxEh0dLVOmTJFGjRrJ448/burQ1//444/y5JNPSt++fcvlswMAAAAAQp+jZi/XIFtVr17dlBp8a/a7d+/e7m1atGgh9evXl/nz55ugW8u2bduagNuigfTNN98sK1askI4dO5ptPOuwttGMd2lyc3PNzZKZmWnKoqIic7P+7XK53PcB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BrJo0SJ59NFHZfz48fLSSy8F5TMCAAAAACqvch3Tfd5555lbaTTL/dRTT8m9994rAwYMMI+99tprUqtWLZMRv/zyy2XVqlUya9Ys+fXXX+Xkk0822zz77LPSr18/eeyxx0wG/c0335S8vDx55ZVXJDo6Wlq3bi1Lly6VJ554olhwDgAAAABApRnTvXHjRtm5c6fpUm5JTk6WLl26yPz58819LbVLuRVwK90+PDzcZMatbc466ywTcFs0W7569WrZt29fUD8TAAAAAKBycezs5RpwK81se9L71nNapqYWX/onMjJSqlevXmybRo0aHVWH9Vy1atWOeu/c3Fxz8+yiroqKiszN+rdm4637CC1hYVJphYUVSViYy5R2m7e/jmO4hEt0fk1T6n75qig8XA5E1zSlnXrgm/I+XXLeRqiibSNU0bbhdN62TccG3eVp0qRJMmHChKMez8jIcI8n1wN84MABcyLQzDpCS3q6VGJFUrPmkba9e3e4Q45jmrTet0ykpv57t8+1FEqa3NN62ZF9s1EPfLO7nA85522EKto2QhVtG06XlZVVsYPutLQ0U+7atcvMXm7R+x06dHBvs7vEX3EFBQVmRnPr9VrqazxZ961tSho7dqyMGTOmWKZbZz1PSUkxs6RbJwGdcV0f4yQQerZskUqe6Q6TrVtTJDXVXtuuzMcRRyvRMSnoOG8jVNG2Eapo23A6XR2rQgfd2iVcg+I5c+a4g2wNfnWs9s0332zud+3aVfbv329mJe/cubN57NtvvzU/UB37bW3z73//W/Lz8yUqKso8pjOdN2/evNSu5SomJsbcStIfu+cPXk8CJR9DaHC5pFJzucLE5bLftv11HLNiV8ivTQfIKes+kcSc1j7XUz9rhfz71wHy31M+kc2JvtcD3zjhVMl5G6GKto1QRduGk3nbLsu19ep62jqTuN6sydP035s3bzY/sFGjRskDDzwgn376qSxbtkyGDh1qZiS/6KKLzPYtW7aUc889V2644QZZuHCh/PTTTzJy5Egzs7lup6688koziZqu361Li7377rvy9NNPF8tkA3C2ovBcORy73pR2RBXlSp3D600JAAAABEO5Zrp/++036dmzp/u+FQhfffXVMn36dLnzzjvNWt66tJdmtLt162aWCPNM4+uSYBpo9+rVy1xpGDhwoFnb23PG86+//lpGjBhhsuE1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mwBbe3C8//77cvXVV5vxrUBFtmXLFrn11lvNPBw6STEQiuheXgY1a9aUiIiIo2ZL1PtpaWnltl+Av1ntmbaOimrkyJHy2Wefydy5c80EVBZtvzpUaP/+/cW2p22jItBsX9OmTaVz585mpn6dDPPpp5+mXaNC0+7jOiFxp06dJDIy0tz0YpJO5qr/1ow27RsVHUF3Gf9np/+jmzNnTrHui3pfu3sBoaJRo0bmf2SebT0zM9PMYk5bh5PpvIAacGu322+//da0ZU96Do+KiirWtnVJsc2bN9O2UeHo3yC5ubm0a1RovXr1MkMntBeHdTv55JNl8ODB7n/TvlHR0b28jHS5MO3OpSeAU089VZ566ikzkcm1115b3rsGlMnBgwdl3bp1xSZP0/+56YRTOjmJjoV94IEHpFmzZiZw+c9//mPW9NZ1jwEndyl/66235JNPPjFrdVvj/XQiQF1vXsthw4aZc7m2dV3v+JZbbjF/uJ122mnlvfvAMY0dO1bOO+88c37Oysoy7fy7776Tr776inaNCk3P1da8GxZdplTX5LYep32joiPoLqPLLrtMMjIy5L777jN/zHXo0EFmzZp11IRTgNPpOq89e/Z039f/mSm9qDR9+nS58847zQWl4cOHmy5d3bp1M22d8VZwshdeeMGUPXr0KPb4tGnT5JprrjH/fvLJJ83KEwMHDjRZQl1//vnnny+X/QW8pd1vhw4dKjt27DBBtq5jrAH3OeecY56nXSOU0b5R0YXpumHlvRMAAAAAAIQixnQDAAAAABAgBN0AAAAAAAQIQTcAAAAAAAFC0A0AAAAAQIAQdAMAAAAAECAE3QAAAAAABAhBNwAAAAAAAULQDQAAAABAgBB0AwBQSUyfPl2qVq3q02v/85//yPDhw8UJevToIaNGjRKncLlc5thUr15dwsLCZOnSpY74zvxh1qxZ0qFDBykqKiq3fQCAio6gGwDgs4yMDLn55pulfv36EhMTI2lpadK3b1/56aefQjrIcnKQZGnYsKE89dRTfqlr586d8vTTT8u///1vv9QXajQw1e/9s88+kx07dkibNm0c2y7K6txzz5WoqCh58803y3tXAKDCiizvHQAAVFwDBw6UvLw8efXVV6Vx48aya9cumTNnjvz999/lvWvwo6lTp8rpp58uDRo0kFBVWFhostTh4WXPR6xfv15q165tjlEouuaaa+SZZ56Rq666qrx3BQAqJDLdAACf7N+/X3744Qd5+OGHpWfPniYgO/XUU2Xs2LFy4YUXFtvu+uuvl5SUFElKSpKzzz5bfv/9d/fz48ePN91XX3/9dZOdTU5Olssvv1yysrLcf/DPmzfPZFo1KNLbX3/9ZZ5bvny5nHfeeZKQkCC1atUyQcGePXuKZcj/9a9/yZ133mm6/momXt+v5Oe48cYbzetjY2NNllIzlpYff/xRzjzzTImLi5P09HRT36FDh2wdNzvHQ+m/Bw8eLFWqVDHB3pNPPlmsN4D+e9OmTTJ69Gj3MfP01VdfScuWLc1x00ymZmeP55133pH+/fsXe+xEx1a/o5JdrfWz62Pfffedua+l3tf96dixoznGejx2794tX375pdlHPUZXXnmlHD58uNj7FxQUyMiRI83xqVmzpun+rt28Lbm5uXL77bdL3bp1zXHq0qWL+309M8+ffvqptGrVyvTU2Lx5c6mfX9uftm3dRo/33Xffbd7fap+33HKLea1+Fv3OStL3vfbaa+XAgQPu78M6Vvv27ZOhQ4dKtWrVJD4+3rTntWvXHrd3ycknnywXX3yx+Yza7XvSpEnSqFEjc/zat28v77//frH31vfTi2H6On0PvTiwevVq9zba/vQ3nJiYaI53586d5bfffnM/r9+93teLCwCAsiPoBgD4RAM2vX388cfmj/9jGTRokDuIWrRokXTq1El69eole/fudW+jf8xrPRrs6k2DnIceesg8p8F2165d5YYbbjDBod40+NUATgM0DdY0INAuvpppv/TSS4u9v2bhNej65Zdf5JFHHpH7779fZs+ebZ7TgEWDHO0O/8Ybb8jKlSvN+0ZERLj3S4NSzej/8ccf8u6775ogXIM9X9k9HmrMmDFmnzVg1M+iFz8WL17sfv7DDz+UevXqmc9qHTOLBq+PPfaYCeq///57EyxqcHosul96XDRgK+l4x7YsNAB97rnn5Oeff5YtW7aY71C7xr/11lvy+eefy9dffy3PPvvsUe8dGRkpCxcuNG3kiSeeMBl5i35H8+fPNxcM9LvT467fpWdAq8dCLxrp61asWCGpqalH7du2bdukX79+csopp5jg9IUXXpCXX35ZHnjgAfO8vrd+bj3eepx//fXXo+rQIFc/jwa01vdhHXMN2rX96nep+6sXDvT98vPzj6pHj41eANILQxpY60UADbhfe+01mTJlivkMeqFlyJAhps140qEBjz/+uHkvPW7XXXed+zm9gKP7r/uubVIvKmiXcosOH9GLUtrOAAA+cAEA4KP333/fVa1aNVdsbKzr9NNPd40dO9b1+++/u5//4YcfXElJSa6cnJxir2vSpInrxRdfNP8eN26cKz4+3pWZmel+/o477nB16dLFfb979+6uW2+9tVgdEydOdPXp06fYY1u2bNFUp2v16tXu13Xr1q3YNqeccorrrrvuMv/+6quvXOHh4e7tSxo2bJhr+PDhxR7Tz6Svyc7OLvU106ZNcyUnJ5f6nD+Ohz4eFRXlmjFjhvv5/fv3m9d4HqMGDRq4nnzyyaP2TY/PunXr3I9NnjzZVatWLdexLFmyxLxm8+bNxR4/0bHduHGjeZ2+3rJv3z7z2Ny5c819LfX+N998495m0qRJ5rH169e7H7vxxhtdffv2LfbeLVu2dBUVFbkf0/fVx9SmTZtcERERrm3bthXbv169epk26nksli5d6jqee+65x9W8efNi76XHLCEhwVVYWGju63HW4308pbWLNWvWmH346aef3I/t2bPHFRcX53rvvfeKve7PP/90paenu/71r3+590XbkX7vP//881Ht9oorrjjmMf7888/NY1YbTkxMdE2fPv24+9+xY0fX+PHjj7sNAKB0ZLoBAD7TDPD27dtNlk6ziNqVVTO32nVXaWbw4MGDUqNGDXdmXG8bN24s1lVVu+Rq11aLduHVbPDxaN1z584tVm+LFi3Mc551t2vXrtjrPOvWrs+a4TvppJOO+R76WTzfQyeK0wy5foay8sfx2LBhg8mCandni3axbt68uVf7oN2LmzRpUmrdpcnOzjaldr0v6XjHtiw869GMqu6jzhHg+VjJek877bRi3ea1N4RmsXVs9rJly0yp36vncdbsr+dxjo6OPuozlLRq1SpTt+d7nXHGGeZ73Lp1a5k/a8m6NeusXd8t2jb0u9TnPL8DzXBfcskl7mEWat26dSZbf8455xT7nJr5LtkV3PNz6vekrGOqPSd0yEPv3r1Nj4rSupFr1/WSXfwBAN5hIjUAgC0ajOkf/XrTcbX6x/u4ceNMt1kNTPQPfM+xtBbPmZw9u7IqDSpOtESR1q1jTbV7cElWUHGiujWQONF76HhvHbtckna5LatAHg9vlVa351joknS8tDX2WMehn6guaz+tCck86y6ty3TJerQOu59fj7MOEdCu0tZQAYsGpRb9/kuOd3ci7UauAbEONbjjjjvMOHXrcyrtgm895vma4x1jZR1T7d6v4+a1Hh32oL9f7Zav48Y9hxmU/P4BAN4h6AYA+JVOSqXjkZVmvXW5Kc3mlTbBlLc0I6mZS09a9wcffGDq1fp9odk/zVauWbOm1Gy3voeOZ27atKnP+16yPrvHQzPAGkDp+Fsr8NcJuvQznHXWWcc9Zr7QrLiORdbjcKweAaWxAjQdv6zj7pU/16/WceSeFixYIM2aNTNBtr6ffnbN5GqG2A6dzE3bmV48sIJVHU+vPRG0l4S3Svs+tG6dkE0/izXzuc78r5Oc6e/IohcwdAy+BsY64ZletKlTp06xCeC6d+9u63Pqd6s3HRN+xRVXyLRp09xBd05Ojsl+W98jAKBs6F4OAPCJBgc6kZlOQKYTVWkX6RkzZpgJtQYMGGC20eycds296KKLzGRYOqO1Tpalkzp5zo58IhqgamCir9fZyTVDN2LECJN90wBBA1ANCnQWbJ0l2ttgUwMVDVS1m7xOAKafQTN9Oimbuuuuu8z+6qRcGjBq9+VPPvnkhBOp6fvr9p437S7sj+Ohwd7VV19tMp7avV4nzxo2bJgJzDyztnrMdKI0nQjMc0b3stJ6db91Army0CyydgHX7sr62bVr97333iv+ooGmdovWAPXtt982E63deuut5jkNHnVyMJ0VXCeV0+9VJ1zTScc0m1sW//znP80EZjpD+Z9//mm+f80E63uXZXkx/T40M62ziOv3oV219SKB/lZ0kkA9vjr8QCdB06y19Ruy6MUEXStbZyfX351evNG2oBOyaaCsE8vpb0An1NNjofe9oV3XtT1rIK8z3usFBf096QUBzwsaGtxr2wUAlB1BNwDAJ9pNV8ei6nJVGrjqjMravVwDCJ2JWmkQ+MUXX5jnNRjWYEiXv9I/7nWcrrc0sNCgQzN7mkHVgEszfRogaIDbp08fadu2rVkyS7tplyUY0iymzkytwbvWr0tgWUG7ZsI1WNQssmZMNdN33333mfc+Hg2udFvPm3aF99fx0Jm6NQC64IILTECsY4w1SPIcd60zamtQr5lqu92CdciAdjcuaxf3V155xWRydQkq/W6sGb/9QQNqDRh1bLtegNGAe/jw4e7nNVOr29x2221mjLRe6PDsHeAtDYD1O9OgXQPem266yVzkKOsFBM1k62svu+wy833oxSlrP/X46Hep36lm1PX9SnaxV9pDQi8wtG7d2r202sSJE83vTi8oaBvQuRX0woIuIeYN/V3pBTQ9VtoedeZ4ndF/woQJ7m30PfUiho61BwCUXZjOpubD6wAAgEPouuEaHOqSUBoQ+pv+qaAXWKyux6g8NCuvFy20J4a3gTwAoDgy3QAAVDBLliwx2UerO7FmIVXJLsn+ohn6l156yWStUblob4nnn3+egBsAbCDTDQBABQy6tcu3jmfWCbq0e7J2Odcu9gAAwFkIugEAAAAACBC6lwMAAAAAECAE3QAAAAAABAhBNwAAAAAAAULQDQAAAABAgBB0AwAAAAAQIATdAAAAAAAECEE3AAAAAAABQtANAAAAAECAEHQDAAAAACCB8f8ANILCcN91YqUAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Read the training source file and analyze sentence lengths\n",
    "with open(\"wmt16/train_src.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "    lines = file.readlines()\n",
    "\n",
    "# Split each line and calculate lengths\n",
    "lengths = [len(line.strip().split()) for line in lines]\n",
    "\n",
    "# Create a histogram of sentence lengths\n",
    "plt.figure(figsize=(10, 6))\n",
    "plt.hist(lengths, bins=50, alpha=0.7, color='blue')\n",
    "plt.xlabel('Sentence Length (number of tokens)')\n",
    "plt.ylabel('Frequency')\n",
    "plt.title('Distribution of Sentence Lengths in Training Data')\n",
    "plt.grid(True, alpha=0.3)\n",
    "\n",
    "# Add some statistics as text\n",
    "plt.axvline(np.mean(lengths), color='r', linestyle='dashed', linewidth=1, label=f'Mean: {np.mean(lengths):.2f}')\n",
    "plt.axvline(np.median(lengths), color='g', linestyle='dashed', linewidth=1, label=f'Median: {np.median(lengths):.2f}')\n",
    "plt.legend()\n",
    "\n",
    "# Display some statistics\n",
    "print(f\"Total sentences: {len(lengths)}\")\n",
    "print(f\"Min length: {min(lengths)}\")\n",
    "print(f\"Max length: {max(lengths)}\")\n",
    "print(f\"Mean length: {np.mean(lengths):.2f}\")\n",
    "print(f\"Median length: {np.median(lengths)}\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f7c22d1a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total sentences: 29000\n",
      "Min length: 4\n",
      "Max length: 41\n",
      "Mean length: 13.25\n",
      "Median length: 13.0\n"
     ]
    },
    {
     "data": {
      "image/png": "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HzaBRd++feWXX34xzz/66KMmf/v27e5zpIGl0gBXz6meK60DPa6VK1ea7f78808TpJ3uOdVx+hok6bnSfTZu3NjUj07Ap70otAeFphdddJHJv/322003XB3Dqi2Eu3bt8sn4c60fDYp1fK0G8/qeGphoQLhs2TKvGw8aXOt27du3NzdxZs+eLS+++KIJivVmgXWudIjAr7/+avL0uL744gvzHp70ePQc6M0wLcPJxtjreF/dVutZ66x3796yadMmdw8PPa96fd99992mrHqt6T71XBenu3Fxr4eiuuaaa6Rhw4ZmngPrJpiWV49Jbx5ocKnHozcbNF20aNEpu5LrNahDWfSzr+dZbzzpddiuXTtz06AwBw8elEsvvdSc13/+85/m86ffWS1atDDfd9ZNPA1e9TOn17WWUetGv2Ps+g4vyjnRMuv199FHH5lrsXLlyl7fL/o51uPXa1e/S6ZPn27qVT+jgwcP9knZAQQ4HdMNAHC5Jk6cqL9MXb/99ttJt4mPj3e1adPG/feIESPMayzjxo0zfycnJ590H7p/3UbfL7+LLrrIPDdhwoQCn9OHZd68eWbbGjVquFJSUtz5U6ZMMfkvv/yyO6927dquAQMGnHKfhZVNX6/7sXz++edm26eeespru3/84x+uoKAg14YNG9x5ul14eLhX3ooVK0z+K6+84irMSy+9ZLb74IMP3HmZmZmuDh06uGJiYryOXct3+eWXu04lNzfXfa6rVKniuv76613jx493bdmy5YRtBw4c6KpWrZpr3759XvnXXXed+TwcO3bMqz6aNGniysjIcG+n9aD5K1eudOdpGT3PpeX99993BQcHu3744QevfP086D5++umn0z6n/fv3N/ss6HOt50GNHj3aVa5cOdeff/7p9fzDDz/sCgkJcW3dutVVGD2XzZo1O+nzqamprvLly7sGDRrklb97925zDj3z9XOmx/Dkk096bavXXbt27dx//+9//zPb6efDkpOT47r44otP+AwPHjzY6zq1bN682eRXqlTJdeDAAXf+F198YfKnT59u/j548KD5+/nnn3edrvzfESW9HjxNnTrVvEY/e/nfTz/T+VmfVU8fffSR2f77778/4btQz49FP6/5t9u7d68rIiLCdd9997nzrOvAs0zWtfbee++58/QaqVq1qqtPnz7uvBdffNFsp98tlrS0NFfj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jfPqDaD/3rJliwwdOtR9zjzNmDFDmjRpYs6btmRq62xhPv74Y+nZs6dX3qnOrdZR/q7WeuyaN3/+fPO3pvq3lqdNmzbmHOv52Lt3r3z77bemjHqObrjhBjl27JjX+2dnZ8uQIUPM+alcubLp/q7dvC0ZGRly//33S40aNcx5at++vft9PVuev/zyS2natKnpqbF169YCj18/f/rZ1m30fD/88MPm/a3P5913321eq8eidZafvu/NN98shw8fdteHda4OHjwo/fv3lwoVKkh0dLT5PK9fv77Q3iVnn322XH311eYYtdv3mDFjpG7duub8tWrVSj799FOv99b305th+jp9D705sG7dOvc2+vnTazg2Ntac73bt2snixYvdz2vd6996cwEAcPoIugEAxaIBmz4+//xz8+P/ZK655hp3ELVkyRJp27atdOnSRQ4cOODeRn/M63402NWHBjnPPPOMeU6D7Q4dOsigQYNMcKgPDX41gNMATYM1DQi0i6+2tP/zn//0en9thdeg65dffpHnnntOnnzySZk1a5Z5TgMWDXK0O/wHH3wga9asMe8bEhLiLpcGpdqi//vvv8snn3xignAN9oqrpOdDDRs2zJRZA0Y9Fr35sXTpUvfz06ZNk5o1a5pjtc6ZRYPXF154wQT133//vQkWNTg9GS2XnhcN2PIr7NyeDg1AX331Vfn5559l27Ztpg61a/zkyZPl66+/lpkzZ8orr7xywnuHhobKr7/+aj4jY8eONS3yFq2jhQsXmhsGWnd63rUuPQNaPRd600hft3r1aklMTDyhbDt27JAePXrIOeecY4LT119/Xd5++2156qmnzPP63nrcer71PP/2228n7EODXD0eDWit+rDOuQbt+vnVutTy6o0Dfb+srKwT9qPnRm8A6Y0hDaz1JoAG3O+9955MmDDBHIPeaOnXr5/5zHjSoQEvvviieS89b7fccov7Ob2Bo+XXsutnUm8qaJdyiw4f0ZtS+jkDABSDCwCAYvr0009dFSpUcEVGRrrOO+881/Dhw10rVqxwP//DDz+44uLiXOnp6V6vq1+/vuuNN94w/x4xYoQrOjralZKS4n7+gQcecLVv397990UXXeS65557vPYxevRoV7du3bzytm3bpk2drnXr1rlfd8EFF3htc84557geeugh8+8ZM2a4goOD3dvnN3DgQNdtt93mlafHpK9JS0sr8DUTJ050xcfHF/icL86H5oeFhbmmTp3qfv7QoUPmNZ7nqHbt2q5x48adUDY9Pxs2bHDnjR8/3lWlShXXySxbtsy8ZuvWrV75pzq3mzdvNq/T11sOHjxo8ubNm2f+1lT/nj17tnubMWPGmLyNGze6826//XZX9+7dvd67SZMmrtzcXHeevq/mqS1btrhCQkJcO3bs8Cpfly5dzGfU81wsX77cVZhHHnnE1ahRI6/30nMWExPjysnJMX/redbzXZiCPhd//vmnKcNPP/3kztu3b58rKirKNWXKFK/X/fHHH66kpCTXv/71L3dZ9HOk9f7zzz+f8Lm9/vrrT3qOv/76a5NnfYZjY2NdkyZNKrT8bdq0cY0cObLQbQAABaOlGwBQbNoCvHPnTtNKp62I2pVVW261667SlsEjR45IpUqV3C3j+ti8ebNXV1XtkqtdWy3ahVdbgwuj+543b57Xfhs3bmye89x3y5YtvV7nuW/t+qwtfGedddZJ30OPxfM9dKI4bSHXYzhdvjgfmzZtMq2g2t3Zol2sGzVqVKQyaPfi+vXrF7jvgqSlpZlUu97nV9i5PR2e+9EWVS2jzhHgmZd/v+eee65Xt3ntDaGt2Do2e+XKlSbVevU8z9r663mew8PDTziG/NauXWv27fle559/vqnH7du3n/ax5t+3tjpr13eLfja0LvU5zzrQFu7evXu7h1moDRs2mNb6Sy65xOs4teU7f1dwz+PUelLWOdWeEzrkoWvXrqZHRUHdyLXrev4u/gCAomEiNQBAiWgwpj/69aHjavXH+4gRI0y3WQ1M9Ae+51hai+dMzp5dWZUGFadaokj3rWNNtXtwflZQcap9ayBxqvfQ8d46djk/7XJ7uuw8H0VV0L49x0Lnp+OlrbHHOg79VPuyymlNSOa574K6TOffj+6jpMev51mHCGhXaWuogEWDUovWf/7x7v5Iu5FrQKxDDR544AEzTt06TqVd8K08z9cUdo6VdU61e7+Om9f96LAHvX61W76OG/ccZpC//gEARUPQDQDwKZ2USscjK2311uWmtDWvoAmmikpbJLXl0pPu+3//+5/Zr+6/OLT1T1sr//zzzwJbu/U9dDxzgwYNil32/Psr6fnQFmANoHT8rRX46wRdegwdO3Ys9JwVh7aK61hkPQ8n6xFQECtA0/HLOu5e+XL9ah1H7mnRokXSsGFDE2Tr++mxa0uuthCXhE7mpp8zvXlgBas6nl57ImgviaIqqD503zohmx6LNfO5zvyvk5zpdWTRGxg6Bl8DY53wTG/aVK9e3WsCuIsuuqhEx6l1qw8dE3799dfLxIkT3UF3enq6af226hEAcHroXg4AKBYNDnQiM52ATCeq0i7SU6dONRNq9erVy2yjrXPaNfeqq64yk2HpjNY6WZZO6uQ5O/KpaICqgYm+Xmcn1xa6wYMHm9Y3DRA0ANWgQGfB1lmiixpsaqCigap2k9cJwPQYtKVPJ2VTDz30kCmvTsqlAaN2X/7iiy9OOZGavr9u7/nQ7sK+OB8a7A0YMMC0eGr3ep08a+DAgSYw82y11XOmE6XpRGCeM7qfLt2vllsnkDsd2oqsXcC1u7Ieu3btfuyxx8RXNNDUbtEaoH700UdmorV77rnHPKfBo04OprOC66RyWq864ZpOOqatuafjrrvuMhOY6Qzlf/zxh6l/bQnW9z6d5cW0PrRlWmcR1/rQrtp6k0CvFZ0kUM+vDj/QSdC01dq6hix6M0HXytbZyfW605s3+lnQCdk0UNaJ5fQa0An19Fzo30WhXdf186yBvM54rzcU9HrSGwKeNzQ0uNfPLgDg9BF0AwCKRbvp6lhUXa5KA1edUVm7l2sAoTNRKw0Cv/nmG/O8BsMaDOnyV/rjXsfpFpUGFhp0aMuetqBqwKUtfRogaIDbrVs3adGihVkyS7tpn04wpK2YOjO1Bu+6f10CywratSVcg0VtRdYWU23pe+KJJ8x7F0aDK93W86Fd4X11PnSmbg2ArrjiChMQ6xhjDZI8x13rjNoa1GtLdUm7BeuQAe1ufLpd3N955x3TkqtLUGndWDN++4IG1Bow6th2vQGjAfdtt93mfl5banWb++67z4yR1hsdnr0DikoDYK0zDdo14L3jjjvMTY7TvYGgLdn62muvvdbUh96cssqp50frUutUW9T1/fJ3sVfaQ0JvMDRr1sy9tNro0aPNdac3FPQzoHMr6I0FXUKsKPS60htoeq7086gzx+uM/qNGjXJvo++pNzF0rD0A4PQF6WxqxXgdAADwE7puuAaHuiSUBoS+pj8V9AaL1fUYzqGt8nrTQntiFDWQBwB4o6UbAIAAs2zZMtP6aHUn1lZIlb9Lsq9oC/2bb75pWq3hLNpb4rXXXiPgBoASoKUbAIAADLq1y7eOZ9YJurR7snY51y72AADAvxB0AwAAAABgE7qXAwAAAABgE4JuAAAAAABsQtANAAAAAIBNCLoBAAAAALAJQTcAAAAAADYh6AYAAAAAwCYE3QAAAAAA2ISgGwAAAAAAmxB0AwAAAAAg9vh/WjXjDHX0PF8AAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Read the training source file and analyze sentence lengths\n",
    "with open(\"wmt16/train_trg.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "    lines = file.readlines()\n",
    "\n",
    "# Split each line and calculate lengths\n",
    "lengths = [len(line.strip().split()) for line in lines]\n",
    "\n",
    "# Create a histogram of sentence lengths\n",
    "plt.figure(figsize=(10, 6))\n",
    "plt.hist(lengths, bins=50, alpha=0.7, color='blue')\n",
    "plt.xlabel('Sentence Length (number of tokens)')\n",
    "plt.ylabel('Frequency')\n",
    "plt.title('Distribution of Sentence Lengths in Training Data')\n",
    "plt.grid(True, alpha=0.3)\n",
    "\n",
    "# Add some statistics as text\n",
    "plt.axvline(np.mean(lengths), color='r', linestyle='dashed', linewidth=1, label=f'Mean: {np.mean(lengths):.2f}')\n",
    "plt.axvline(np.median(lengths), color='g', linestyle='dashed', linewidth=1, label=f'Median: {np.median(lengths):.2f}')\n",
    "plt.legend()\n",
    "\n",
    "# Display some statistics\n",
    "print(f\"Total sentences: {len(lengths)}\")\n",
    "print(f\"Min length: {min(lengths)}\")\n",
    "print(f\"Max length: {max(lengths)}\")\n",
    "print(f\"Mean length: {np.mean(lengths):.2f}\")\n",
    "print(f\"Median length: {np.median(lengths)}\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "bbd86f3e4db77521",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-07-11T08:05:12.693773Z",
     "start_time": "2025-07-11T08:05:12.554874Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total sentences: 29000\n",
      "Min length: 16\n",
      "Max length: 215\n",
      "Mean length: 63.07\n",
      "Median length: 60.0\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA94AAAJOCAYAAABBfN/cAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAfT5JREFUeJzt3Qd0VNXWwPGdXkihhR460ruKKFUQFB+i2AWxIDawgBWfiohP7B1Fnwp2BTs2FERUBAtFqkiT3lsSSM98ax++mTcTQnITcpOT5P9bazxk5sydO/fMHWfffUqQx+PxCAAAAAAAcEWwO5sFAAAAAACKwBsAAAAAABcReAMAAAAA4CICbwAAAAAAXETgDQAAAACAiwi8AQAAAABwEYE3AAAAAAAuIvAGAAAAAMBFBN4AAAAAALiIwBsAjsMDDzwgQUFBJfJavXr1MjevH374wbz2hx9+WCKvf+WVV0rDhg3FZikpKXLNNddIrVq1zLG59dZbS3uXYLF//vnHfE6eeOKJcvEdUVymTp1q9lmPT2F5v5e0BAD8D4E3AOT6sem9RUZGSp06daR///7y3HPPSXJycrG8zrZt28yP8SVLlohtbN43Jx5++GHTjjfccIO89dZbcvnllx+zbkZGhjz77LPSsWNHiYuLk8qVK0vr1q3l2muvlb/++svV/Xz33XflmWeekfJCLwi1adNGbPXVV1+Zz3V5OM7+31HHupWH92rbd/gvv/xijuuBAweKdZ8BVBxBHo/HU9o7AQC2/Gi76qqr5MEHH5RGjRpJZmam7Nixw2RuvvvuO6lfv758/vnn0q5dO99zsrKyzE1/4Dn1xx9/yEknnSRTpkwxWWSnNFBU4eHhptT96t27t0yfPl0uuOCCQr3XouybHo+cnByJiIgQW51yyikSGhoqP//8c4F1Bw4cKF9//bVceuml0rVrV/P+NOD+4osvZMKECYVqm8L617/+JcuXLy9SRtHWgHDPnj3mPdlo1KhRMmnSJMn9k0ePv57rjz/+uNx+++2uvHZRviOORb+Hdu7c6fv7999/NwHlPffcIy1btvTdr99R/t9ThZWdnW3OBz3XC5ut1+8I/a7S76ng4GDrv8Od0l4Rd9xxh2zYsMH6nj8A7BRa2jsAALY566yz5MQTT/T9PXbsWPn+++9NsHTOOefIqlWrJCoqyjymQZ7e3HT48GGJjo72BdylJSwsTGy3a9cuadWqVYH1NGDRAPs///mPCVr8vfDCC2S1UGyK8zvijDPOCPhbg3kNvPV+/2EouR06dEgqVark+HVCQkLMrSg02C6Oiwwl9R0OACWFruYA4MDpp58u9913n2zcuFHefvvtfMdvamalW7duputyTEyMNG/e3BfcaeZFM8pKMzPeLpGaqfHvsrtw4ULp0aOHCbi9z809xts/O6V1dFyz/rjWH5abN28OqKMZmrwyuP7bLGjf8hrjrT/ob7vtNklMTDTZMX2vmhnKnVnU7WjW8dNPPzXvT+tqt+5vvvnGcUA9fPhwqVmzpvlR3759e3njjTeOGleq2agvv/zSt+/HyiivW7fOlKeddtpRj2nAUa1atYD7tm7dKldffbV5fe++v/766wF1vPswbdo0E9DXq1fP7GufPn1k7dq1Acdc91E/S9799D+u6enpMm7cOGnatKl5LT22d955p7m/qMdU91+Pn3a71XqaDdTu+N5eFEovNuiYeG9b6us/+uijJoNZXLSHQffu3c3nNDY2Vs4++2xZsWJFQB39nOl5o/t87rnnmn8nJCSYjLR+1v3t3bvXDCfwDhW44oor5M8//zzqc6vZbu8x895ye+WVV6RJkybmvet5oBdn/GnmVM8LbVetU7t2bRk0aFCBvRby+o443vPByeutXLlSLrvsMqlSpYr5PlJLly41x6Nx48bms6nfGfq51uNY0Bhv/Yxq4Kq9SU4++WTzfN3Om2++WeAYb+/3mu6T9tLR77W6devKY489dtT+63mh32H6GalRo4aMHj1aZs6cedzjxo/1He7kmOgx1Wy30nMn9/eL9hDS7ev+alvqxb+XXnqpyPsKoHwi4w0ADukPfA1wv/32WxkxYkSedTSI0B+n2pVRuzvqjzANuubNm2ce1+6gev/9999vxhJrEKJOPfVU3zb0B59mbC655BIZOnSoCfbyo0Ge/gi86667TICqY4f79u1rxmkXJqvjZN/8aXCtP5DnzJljgroOHTqYH8j6A1WDpqeffjqgvv5g//jjj+XGG280QZdm6s4//3zZtGnTUYGuv9TUVPPDXY+jBiv6w1e71+uPZQ0Wb7nlFrPvOqZbf6RrYKQXA5QGbHlp0KCBKd955x0TfOeXkdSuvdqF3Rss6TY1gNT3nJSUdNQEbo888ojJ+mmgePDgQRNcDBkyRH799Vfz+L///W9z/5YtW3zHSINLpUGuHlM9VtoG+r6WLVtm6v39998mUCvsMdVx+xoo6bHSbbZo0cK0j07Kp70ptCeFlj179jT3X3fddaZLro5p1Uzh9u3bi2U8uraPBsY63lYDen1NDU40KFy8eHHAxQcNsLVely5dzIWcWbNmyZNPPmkCY71g4D1WOlzgt99+M/fp+/rss8/Ma/jT96PHQC+I6T4ca8y9jv/VutrO2maDBw+W9evX+3p66HHV8/umm24y+6rnmm5Tj3VRuh4X9Xxw6sILL5RmzZqZeQ+8F8J0f/U96QUEDTD1/egFBy0XLFhQYLdyPQd1WIt+9vU468UnPQ87d+5sLhzkZ//+/XLmmWea43rRRReZz59+Z7Vt29Z833kv5GkAq585Pa91H7Vt9DvGre9wJ8dE91nPv/fee8+ci9WrVw/4ftHPsb5/PXf1u2TGjBmmXfUzOnLkyGLZdwDlgI7xBgB4PFOmTNFfp57ff//9mHXi4+M9HTt29P09btw48xyvp59+2vy9e/fuY25Dt6919PVy69mzp3ls8uTJeT6mN685c+aYunXr1vUkJSX57p82bZq5/9lnn/Xd16BBA88VV1xR4Dbz2zd9vm7H69NPPzV1H3rooYB6F1xwgScoKMizdu1a331aLzw8POC+P//809z//PPPe/LzzDPPmHpvv/22776MjAxP165dPTExMQHvXffv7LPP9hQkJyfHd6xr1qzpufTSSz2TJk3ybNy48ai6w4cP99SuXduzZ8+egPsvueQS83k4fPhwQHu0bNnSk56e7qun7aD3L1u2zHef7qP/sfR66623PMHBwZ6ffvop4H79POg25s2bV+hjOmzYMLPNvD7XehzUhAkTPJUqVfL8/fffAY/ffffdnpCQEM+mTZs8+dFj2bp162M+npyc7KlcubJnxIgRAffv2LHDHEP/+/Vzpu/hwQcfDKir513nzp19f3/00Uemnn4+vLKzsz2nn376UZ/hkSNHBpynXhs2bDD3V6tWzbNv3z7f/Z999pm5f8aMGebv/fv3m78ff/xxT2Hl/o443vPB3/Tp081z9LOX+/X0M52b97Pq77333jP1f/zxx6O+C/X4eOnnNXe9Xbt2eSIiIjy33Xab7z7veeC/T95z7c033/Tdp+dIrVq1POeff77vvieffNLU0+8Wr9TUVE+LFi2O2mZxfYc7PSba9rmPSX7b6N+/v6dx48b57i+AioWu5gBQCJqZzG9mXO3uqjTzVtQuupol1+yLU8OGDTMZMy/NSGk3WJ3J2U26fe2WffPNNwfcr9lmjS00K+xPs/CasfTSXgHaRVizTQW9jmaidBI0L81C6uvq8mFz584t9L5rFkuz8w899JDpiquZLM1MaSb84osv9o3x1vfx0Ucfmcyq/lsnEPPeNCOrmetFixYFbFvbzn88vrfnQEHvU2kmX7Pcmr31fy3NAqrcmb+Cjql+BjVLrvvvP+bV/zh4X1f3U4+F/+vq9jX7/OOPP8rx0KyiHlNtQ//t6+dHs9p5ZTSvv/76gL91//yPoXbL1s+Bf+8T7WlQlAyjtrm+d//XUt7X054j2qba1Vkzt8WhqOeDU7mPn/LvAZOWlmbaQHtzqNyf47xoF2rvsfFmfHV4iZN91u9O7cHjpcdTe2LkblPtgq6ZYy/t/n2sHkbF8R1+vMck9zb0O0G3oT1I9L3p3wCg6GoOAIWggZ6O48vvB/yrr75q1pK+++67zfhe7aaowbDTGX71h2dhJlLT7qS5gykdn+v2jNk6VlLHDPsH/co7u7I+7k+7L+emwU5BgYxuR99j7uN3rNcpzAUO7fatN+3aqgG8Li+mY7Q1oNNxoLt37zYBo3Y91VtetMtxfu/TG9A5CdjWrFljJn46Vhf5gl7L+3re19L91+7wBS31pa+rY12dvm5h6faV9wJCbhpw+tNgK/e+5P6saLvrBSYdL+xPP/uFVVCb6WdFu8frRSUd+qGBmQ4p0YteelGoKIp6PjilQzJy27dvn4wfP17ef//9o9rUSYB4PPusQ0Byd2XX5+rnzr9N9WJE7npFaVOn3+HHe0yUDiXSeRnmz59vhlDk3kZ8fHwx7T2AsozAGwAc0jG5+iMqvx+BmvnQ7KBm8HQCLc3gfPDBBybg0HGFTmYKdmO23WON3dRsZlFnLy6sY72ODataagCnY+p1jK2O1dTgWyeY8vZa0Exd7rHDXrmXJjqe96mvp2Nen3rqqTwf14nPiuu1cr+uzoytk7jl5YQTTijU9vLavtIx1nkFqrnH2JfUZ7Iwx1HH8mvPAe1BoL0ldKKuiRMnmtmydS14N17zeOT1PaJjq3Xsvs7DoHMyaPZX20bHXjvpoXM8+2zD+Z/Xd/jxHhOdqFEvsGovFT1v9RzVC6faU0fHgxfn5IQAyjYCbwBwyDsxk3Yxzo9mZvWHmN70h5hObqRZVQ3GtXtpYdfFdZpN9P8hq5Mg+QeEmlnKa4kszTDpbL5ehdk37Zatk15pt03/rLeuhe19vDjodjQrpj9g/bPexf06SjPdetz0mGp3Uc266nvTCxTadsXlWMdZs306K7d+dorjc6L7r9nkgtbX1tfVTGBxvsfc21eaaSyu19B213PKu9yel/8M8l7Fdc7p+9Cst970M6KBmk765j9Ltq00Kz179myT3dUJFI/1/VGatE115nP9DvNvs7zatDi+wwtzTI71GdKJ1HTFAV0f3L9HQHFNCAeg/GCMNwA4oFmtCRMmmO6bOkP1sWi3xdz0x7nyLgflXU+3uNaK1uV8/Mcs6mzB2nXaO1OwN2DQGXr9l4/SdaxzLztWmH0bMGCACUh13Wt/muXRH6n+r3889HV0KSftOeCVlZUlzz//vMlO6VjKwtIf1jp7dG76vrW7qF6o0KBVs3SaBddx3nkFr9qVuyj0OOfVjVWzbzqz+H//+988Z3fXWZ8LQy9U6JJcGhz88ccfx8w26uvq+9ZMbl7HRI/38dBARy8A6EWozMzMYjmOuk3dlv+x0osz3qXD/B3vOafBvY7/9afnlF6Uyb3Mm628GefcGebimLG+uGib6udfg1gvPe55nQ/F8R1emGNyrM9QXtvQc1uXGAMAf2S8ASAXnRRMs6kabOhSUvqDTSeH0myM/iDU8afHostxaVdzXZ9Y6+uYwRdffNGMb/Supas/2HUStsmTJ5sf7vqDTieYymtMphNVq1Y129ZJvXR/9UejdqX0n5BIx5xrQK7dJzXI0u6RmqXzn9ypsPum3W51TV7N5ut4cl1bW7vT68Ry2i0397aLSpfAevnll82yRbq+uS7dpO9Fx1Xqe809xtwJzSrrGsd6cUAni9JjqD/4dW1wXXpKt+v9Qa3Lg2n2So+DHlOdYEovsOjES5rxz+tiS0F0+SW9kDBmzBizZrReQNDjqcsdaTd3nRhLX1OXOtOLG/p51Ps1MM5rkrT8aLCr7aIXKLxLlOmFGZ1QTZe00vbWbrb62dZxy97loTTI16XM9Fhr+3qXUDoWDZ51srrcvIGOLrmk769Tp06mW79e2NCLHzokQ99n7gs4BdELCjo5l2afNSOqXX31PXjbwz9Dqe9H6YR8Gtxp2+o+OKVLSWkvBD13tP21a/wnn3xizrfCbKc06YWPHj16mKXS9IKFziWhn4sNGzaILXQ5N/0c6CR8upyYDgHRJf+837lOey44/Q4vzDHxfob0+07bXHvH6Dnbr18/07Vc/637rz1H9EKB9u7Q8wwAfEp7WnUAsIV3KRrvTZf70eVuzjjjDLMklP+yVcdaKmj27NmeQYMGeerUqWOer6Uu65N7mSZdrqhVq1ae0NDQgKWP8luW6VjLienSN2PHjvXUqFHDExUVZZaqymtZLF2qR5ce0+V/TjvtNM8ff/xx1Dbz27fcy4l5l4kaPXq0eZ9hYWGeZs2amWV3vMtUeel2dEmn3I61zFluO3fu9Fx11VWe6tWrm+Patm3bPJc8c7qcmG7vkUceMe9dlwrT91qlShWzFNWHH36YZ33d/8TERPM+9XPRp08fzyuvvHJUe+gST3ktWeW/vykpKZ7LLrvMLLGlj/kfV10q7dFHHzWfA20r3S9dRmv8+PGegwcPFumY6udBlxVLSEgw29RljvS5/sueaVvq56hp06bmGOuxPvXUUz1PPPGE2af8eJeLyuumx8n/GOkyS7qkU2RkpKdJkyaeK6+80nwWvXTfdWkzJ8ty6bJ9ehxjY2PNNnVbuuSa1nv//fd99bKysjw33XSTef+61J13O962yWuZML1fX1PpUnJ6vHRZK903fa0uXbqYpfuKupzY8ZwPTpYTy2tJwy1btnjOO+8887nT93DhhRd6tm3bFvBe81tOLK9z61jfS7mXE8vrey2v75T169eb19HvMm0vXarMu3TcggULiv073Okx8S67p9+hujyf//H5/PPPPe3atTOf6YYNG5rz9/XXXz/m8mMAKqYg/c//wnAAAICySyc/O++880w2XzPpKPu0B8ro0aPN5GialQaAsojAGwAAlEk67t1/9m7tlq9df3U8u84L4MYKASjZNtUx3jprvLatdvkHgLKKMd4AAKBMuummm0yg1rVrVzPJ2ccff2yWhtJx7QTdZdPgwYPN7OA6KaVOUqZzUeh4bR3rDQBlGRlvAABQJr377rtmOS+dXE0zozqp4A033CCjRo0q7V3DcXQrf/XVV82Efprl1snsdH35iy++uLR3DQCOC4E3AAAAAAAuYh1vAAAAAABcROANAAAAAICLmFzNgZycHNm2bZvExsZKUFBQae8OAAAAAKCU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rz/qsZEjR0p4eLhcddVVxfqaF198sfxdiqvAfPnll9KlSxeJioqSKlWqyLnnnhvw+KZNm+Tss8+W6OhoqVGjhtxxxx2SlZWV7zb37dsnQ4YMkbi4OHNRYfjw4ZKSa8LdpUuXSvfu3SUyMlISExPlsccek5JA4A3AansO75FXF71qSmdP2CPy6qtHSgAAgDJCg8D3339fUlNTffelpaXJe++9J/Vd6MmnAa8GtKXho48+kssvv9xcTPjzzz9l3rx5ctlll/kez87ONkF3RkaG/PLLL/LGG2+YDP3999+f73Y16F6xYoV899138sUXX8iPP/4o1157re/xpKQk6devnzRo0EAWLlwojz/+uDzwwAPyyiuviNsIvAFYbdPBTTJixghTOnvCJpERI46UAAAAZUSnTp1M8P3xxx/77tN/a9Ddvn37gLo5OTkyceJEadSokQmg9fEPP/wwoM5XX30lJ5xwgnm8d+/e8s8//wQ8nrur+bp162TQoEFSs2ZNiYmJkZNOOklmzZoV8JyGDRvKww8/LFdffbXExsaafSts0JqVlSW33HKLCXo1w6/72KpVK7nooot8db799ltZuXKlvP3229KhQwc566yzZMKECTJp0iQTjOdl1apV8s0338irr75qMundunWT559/3lzM2LZtm6nzzjvvmOe//vrr0rp1a7nkkkvk5ptvlqeeekrcRuANAAAAABbQgHbKlCm+vzVA1G7ouWnQ/eabb8rkyZNNhnf06NEydOhQmTt3rnl88+bNMnjwYBk4cKAsWbJErrnmGrn77rvzfW3tkj1gwACZPXu2LF68WM4880zzfO3y7e/JJ5+UE0880dS58cYb5YYbbpDVq1f7Hu/Vq1ee++y1aNEi2bp1qwQHB0vHjh2ldu3aJrBevny5r878+fOlbdu25iKAV//+/U3GWt9vXvQ5eiFB982rb9++5nV+/fVXX50ePXqYrvv+29X9379/v7gp1NWtAwAAAEAp2568XbanbA+4r0pkFWlUpZGkZaXJyt0rj3pOp9qdTLl6z2o5lHko4LGGlRtK1aiqsvvQbtmctDngsdjwWGlWrVmR9lOD57Fjx8rGjRvN39oFW7uaz5kzx1cnPT3dZJ01G921a1dzX+PGjeXnn3+Wl19+WXr27CkvvfSSNGnSxATJqnnz5rJs2TJ59NFHj/namjX3z6xrhvmT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",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Read the training source file and analyze sentence lengths\n",
    "with open(\"wmt16/train_trg.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "    lines = file.readlines()\n",
    "\n",
    "# Split each line and calculate lengths\n",
    "lengths = [len(line.strip()) for line in lines]\n",
    "\n",
    "# Create a histogram of sentence lengths\n",
    "plt.figure(figsize=(10, 6))\n",
    "plt.hist(lengths, bins=50, alpha=0.7, color='blue')\n",
    "plt.xlabel('Sentence Length (number of tokens)')\n",
    "plt.ylabel('Frequency')\n",
    "plt.title('Distribution of Sentence Lengths in Training Data')\n",
    "plt.grid(True, alpha=0.3)\n",
    "\n",
    "# Add some statistics as text\n",
    "plt.axvline(np.mean(lengths), color='r', linestyle='dashed', linewidth=1, label=f'Mean: {np.mean(lengths):.2f}')\n",
    "plt.axvline(np.median(lengths), color='g', linestyle='dashed', linewidth=1, label=f'Median: {np.median(lengths):.2f}')\n",
    "plt.legend()\n",
    "\n",
    "# Display some statistics\n",
    "print(f\"Total sentences: {len(lengths)}\")\n",
    "print(f\"Min length: {min(lengths)}\")\n",
    "print(f\"Max length: {max(lengths)}\")\n",
    "print(f\"Mean length: {np.mean(lengths):.2f}\")\n",
    "print(f\"Median length: {np.median(lengths)}\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00f8629e",
   "metadata": {},
   "source": [
    "# LangPairDataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "30745b5f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load train dataset from wmt16\\.cache\\de2en_train_273.npy\n",
      "load val dataset from wmt16\\.cache\\de2en_val_273.npy\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "\n",
    "\n",
    "\n",
    "class LangPairDataset(Dataset):\n",
    "\n",
    "    def __init__(\n",
    "        self, mode=\"train\", max_length=273, overwrite_cache=False, data_dir=\"wmt16\",\n",
    "    ):\n",
    "        self.data_dir = Path(data_dir) #封装为path对象\n",
    "        cache_path = self.data_dir / \".cache\" / f\"de2en_{mode}_{max_length}.npy\" # 缓存路径\n",
    "\n",
    "        if overwrite_cache or not cache_path.exists(): #判断是否需要覆盖缓存，或者缓存文件不存在\n",
    "            cache_path.parent.mkdir(parents=True, exist_ok=True) # 创建缓存目录\n",
    "\n",
    "            with open(self.data_dir / f\"{mode}_src.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "                self.src = file.readlines() # 读取源语言文件所有行\n",
    "\n",
    "            with open(self.data_dir / f\"{mode}_trg.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "                self.trg = file.readlines() # 读取目标语言文件所有行\n",
    "\n",
    "            filtered_src = []\n",
    "            filtered_trg = []\n",
    "            # max length filter,超出最大长度的句子舍弃\n",
    "            for src, trg in zip(self.src, self.trg):\n",
    "                if len(src) <= max_length and len(trg) <= max_length: # 过滤长度超过最大长度的句子\n",
    "                    filtered_src.append(src.strip()) # 去掉句子前后的空格\n",
    "                    filtered_trg.append(trg.strip())\n",
    "            filtered_src = np.array(filtered_src)\n",
    "            filtered_trg = np.array(filtered_trg)\n",
    "            np.save(\n",
    "                cache_path,\n",
    "                {\"src\": filtered_src, \"trg\": filtered_trg },\n",
    "                allow_pickle=True,\n",
    "            )#allow_pickle=True允许保存对象数组，将过滤后的数据保存为 NumPy 数组，存储在缓存文件中\n",
    "            print(f\"save cache to {cache_path}\")\n",
    "\n",
    "        else:\n",
    "            cache_dict = np.load(cache_path, allow_pickle=True).item() #allow_pickle=True允许保存对象数组\n",
    "            print(f\"load {mode} dataset from {cache_path}\")\n",
    "            filtered_src = cache_dict[\"src\"]\n",
    "            filtered_trg = cache_dict[\"trg\"]\n",
    "\n",
    "        self.src = filtered_src\n",
    "        self.trg = filtered_trg\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        return self.src[index], self.trg[index]\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.src)\n",
    "\n",
    "\n",
    "train_ds = LangPairDataset(\"train\")\n",
    "val_ds = LangPairDataset(\"val\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "10296400",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "29000"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(train_ds)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "860a6365",
   "metadata": {},
   "source": [
    "# Vocab词典"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "7e42d55f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "7abe437a571844f98a75737e184f6326",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/18107 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "vocab_size: 18111\n"
     ]
    }
   ],
   "source": [
    "#载入词表，看下词表长度，词表就像英语字典,构建word2idx和idx2word\n",
    "word2idx = {\n",
    "    \"[PAD]\": 0,     # 填充 token\n",
    "    \"[BOS]\": 1,     # begin of sentence\n",
    "    \"[UNK]\": 2,     # 未知 token\n",
    "    \"[EOS]\": 3,     # end of sentence\n",
    "}\n",
    "idx2word = {value: key for key, value in word2idx.items()}\n",
    "index = len(idx2word)\n",
    "threshold = 1  # 出现次数低于此的token舍弃\n",
    "\n",
    "with open(\"wmt16/vocab\", \"r\", encoding=\"utf8\") as file:  # 打开词汇表文件进行读取，使用utf8编码\n",
    "    for line in tqdm(file.readlines()):  # 使用tqdm显示进度条，遍历文件的每一行\n",
    "        token, counts = line.strip().split()  # 去除每行两端的空白字符并按空格分割为token和出现次数\n",
    "        if int(counts) >= threshold:  # 判断token的出现次数是否大于或等于阈值\n",
    "            word2idx[token] = index  # 将token添加到word2idx字典，建立token到索引的映射\n",
    "            idx2word[index] = token  # 将token添加到idx2word字典，建立索引到token的映射\n",
    "            index += 1  # 索引值增加1，为下一个token准备\n",
    "\n",
    "vocab_size = len(word2idx)  # 计算词表大小，即word2idx字典中的条目数量\n",
    "print(\"vocab_size: {}\".format(vocab_size))  # 打印词表大小\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e77c79c",
   "metadata": {},
   "source": [
    "# Tokenizer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "656066ff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "raw text\n",
      "['hello', 'world']\n",
      "['a', 'little', 'boy', 'playing', 'gam@@', 'e@@', 'cu@@', 'be', 'at', 'a', 'mcdonald', '&apos;s', '.']\n",
      "['this', 'is', 'a', 'test']\n",
      "indices\n",
      "tensor([   1, 9458, 3522,    3,    0,    0,    0,    0,    0,    0,    0,    0,\n",
      "           0,    0,    0])\n",
      "tensor([   1,    5,  105,   56,   63, 8664,  732, 2716,  414,   35,    5, 4016,\n",
      "         192,    4,    3])\n",
      "tensor([   1,  385,   18,    5, 5699,    3,    0,    0,    0,    0,    0,    0,\n",
      "           0,    0,    0])\n",
      "decode text\n",
      "[BOS] hello world [EOS] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD]\n",
      "[BOS] a little boy playing gam@@ e@@ cu@@ be at a mcdonald &apos;s . [EOS]\n",
      "[BOS] this is a test [EOS] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD]\n"
     ]
    }
   ],
   "source": [
    "class Tokenizer:\n",
    "    def __init__(self, word2idx, idx2word, max_length=128, pad_idx=0, bos_idx=1, eos_idx=3, unk_idx=2):\n",
    "        self.word2idx = word2idx\n",
    "        self.idx2word = idx2word\n",
    "        self.max_length = max_length\n",
    "        self.pad_idx = pad_idx\n",
    "        self.bos_idx = bos_idx\n",
    "        self.eos_idx = eos_idx\n",
    "        self.unk_idx = unk_idx\n",
    "\n",
    "    def encode(self, text_list, padding_first=False, add_bos=True, add_eos=True, return_mask=False):\n",
    "        \"\"\"如果padding_first == True，则padding加载前面，否则加载后面\"\"\"\n",
    "        max_length = min(self.max_length, add_eos + add_bos + max([len(text) for text in text_list]))\n",
    "        indices_list = []\n",
    "        #text_list是句子列表，每个句子是一个单词列表,text_list是一个批次\n",
    "        for text in text_list:\n",
    "            indices = [self.word2idx.get(word, self.unk_idx) for word in text[:max_length - add_bos - add_eos]]\n",
    "            if add_bos:#如果add_bos == True，则添加[BOS]\n",
    "                indices = [self.bos_idx] + indices\n",
    "            if add_eos:#如果add_eos == True，则添加[EOS]\n",
    "                indices = indices + [self.eos_idx]\n",
    "            if padding_first:#如果padding_first == True，则padding加载前面，否则加载后面\n",
    "                indices = [self.pad_idx] * (max_length - len(indices)) + indices\n",
    "            else:\n",
    "                indices = indices + [self.pad_idx] * (max_length - len(indices))\n",
    "            indices_list.append(indices)\n",
    "        input_ids = torch.tensor(indices_list)\n",
    "        masks = (input_ids == self.pad_idx).to(dtype=torch.int64) # 为了方便损失计算，这里的mask为0的地方需要计算，为1的地方不需要计算\n",
    "        return input_ids if not return_mask else (input_ids, masks)\n",
    "\n",
    "\n",
    "    def decode(self, indices_list, remove_bos=True, remove_eos=True, remove_pad=True, split=False):\n",
    "        text_list = []\n",
    "        for indices in indices_list:\n",
    "            text = []\n",
    "            for index in indices:\n",
    "                word = self.idx2word.get(index, \"[UNK]\")\n",
    "                if remove_bos and word == \"[BOS]\":\n",
    "                    continue\n",
    "                if remove_eos and word == \"[EOS]\":\n",
    "                    break\n",
    "                if remove_pad and word == \"[PAD]\":\n",
    "                    break\n",
    "                text.append(word)\n",
    "            text_list.append(\" \".join(text) if not split else text) #如果split == True，则返回一个单词列表，否则返回一个句子\n",
    "        return text_list #返回一个句子列表\n",
    "\n",
    "\n",
    "tokenizer = Tokenizer(word2idx=word2idx, idx2word=idx2word)\n",
    "\n",
    "\n",
    "raw_text = [\"hello world\".split(), \"a little boy playing gam@@ e@@ cu@@ be at a mcdonald &apos;s .\".split(), \"this is a test\".split()]\n",
    "indices = tokenizer.encode(raw_text, padding_first=False, add_bos=True, add_eos=True)\n",
    "decode_text = tokenizer.decode(indices.tolist(), remove_bos=False, remove_eos=False, remove_pad=False)\n",
    "print(\"raw text\")\n",
    "for raw in raw_text:\n",
    "    print(raw)\n",
    "print(\"indices\")\n",
    "for index in indices:\n",
    "    print(index)\n",
    "print(\"decode text\")\n",
    "for decode in decode_text:\n",
    "    print(decode)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "8ef05ff7",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SampleInfo: #下面的info对象\n",
    "    def __init__(self, i, lens):\n",
    "        \"\"\"\n",
    "        记录文本对的序号和长度信息\n",
    "        输入：\n",
    "            - i (int): 文本对的序号。\n",
    "            - lens (list): 文本对源语言和目标语言的长度\n",
    "        \"\"\"\n",
    "        self.i = i\n",
    "        # 加一是考虑填补在文本前后的特殊词元，lens[0]和lens[1]分别表示源语言和目标语言的长度\n",
    "        self.max_len = max(lens[0], lens[1]) + 1\n",
    "        # self.max_len = lens[0] + 1\n",
    "        self.src_len = lens[0] + 1\n",
    "        self.trg_len = lens[1] + 1\n",
    "\n",
    "# 一个批量生成器，根据词元数目的限制来控制批量的大小。它会根据传入的样本信息，在不超过设定大小的情况下，逐步构建批量。\n",
    "#就是箱子\n",
    "class TokenBatchCreator:\n",
    "    def __init__(self, batch_size):\n",
    "        \"\"\"\n",
    "        参数:\n",
    "        batch_size (int): 用于限制批量的大小。箱子大小 高*宽\n",
    "        功能:\n",
    "        初始化了一个空的批量列表 _batch。\n",
    "        设定了初始的最大长度为 -1。\n",
    "        存储了传入的 batch_size。\n",
    "        \"\"\"\n",
    "\n",
    "        self.__batch = []  #这个就是之前的batch_size，就是第一个batch内有多少个样本\n",
    "        self.max_len = -1  #箱子内最长的样本长度\n",
    "        self._batch_size = batch_size # 限制批量的大小,假设是4096\n",
    "\n",
    "    def append(self, info: SampleInfo):\n",
    "        \"\"\"\n",
    "        参数:\n",
    "        info (SampleInfo): 文本对的信息。\n",
    "        功能:\n",
    "        接收一个 SampleInfo 对象，并根据其最大长度信息更新当前批量的最大长度。\n",
    "        如果将新的样本加入批量后超过了批量大小限制，它会返回已有的批量并将新的样本加入新的批量。\n",
    "        否则，它会更新最大长度并将样本添加到当前批量中。\n",
    "        \"\"\"\n",
    "        # 更新当前批量的最大长度\n",
    "        cur_len = info.max_len # 当前样本的长度\n",
    "        max_len = max(self.max_len, cur_len) # 每来一个样本，更新当前批次的最大长度\n",
    "        # 如果新的样本加入批量后超过大小限制，则将已有的批量返回，新的样本加入新的批量\n",
    "        if max_len * (len(self.__batch) + 1) > self._batch_size:\n",
    "            self.__batch, result = [], self.__batch # 保存当前的batch，并返回,这里的result是之前的batch,_batch清空\n",
    "            self.__batch.append(info) #箱子里的第一条样本，放入\n",
    "            self.max_len = cur_len #因为是当前batch的第一个样本，所以它的长度就是当前长度\n",
    "            return result\n",
    "        else:\n",
    "            self.max_len = max_len\n",
    "            self.__batch.append(info) # 将样本添加到当前批量中\n",
    "            return None\n",
    "\n",
    "    @property\n",
    "    def batch(self):\n",
    "        return self.__batch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "f2f41c51",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 当一个自定义对象可以iter(自定义对象)时，会自动调用__iter__方法\n",
    "from torch.utils.data import BatchSampler\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "class TransformerBatchSampler(BatchSampler):\n",
    "    def __init__(self,\n",
    "                 dataset,\n",
    "                 batch_size,\n",
    "                 shuffle_batch=False,\n",
    "                 clip_last_batch=False,\n",
    "                 seed=0):\n",
    "        \"\"\"\n",
    "        批量采样器\n",
    "        输入:\n",
    "            - dataset: 数据集\n",
    "            - batch_size: 批量大小\n",
    "            - shuffle_batch: 是否对生成的批量进行洗牌\n",
    "            - clip_last_batch: 是否裁剪最后剩下的数据\n",
    "            - seed: 随机数种子\n",
    "        \"\"\"\n",
    "        self._dataset = dataset #数据集\n",
    "        self._batch_size = batch_size #箱子大小\n",
    "        self._shuffle_batch = shuffle_batch #是否打乱箱子\n",
    "        self._clip_last_batch = clip_last_batch #是否裁剪最后剩下的数据\n",
    "        self._seed = seed #下面3个是为了随机\n",
    "        self._random = np.random\n",
    "        self._random.seed(seed)\n",
    "\n",
    "        self._sample_infos = [] #所有样本信息\n",
    "        # 根据数据集中的每个样本，创建了对应的 SampleInfo 对象，包含了样本的索引和长度信息。\n",
    "        for i, data in enumerate(self._dataset):\n",
    "            lens = [len(data[0]), len(data[1])] #输入和输出的长度计算放到lens中\n",
    "            self._sample_infos.append(SampleInfo(i, lens)) #每个样本信息\n",
    "\n",
    "    def __iter__(self):\n",
    "        \"\"\"\n",
    "        对数据集中的样本进行排序，排序规则是先按源语言长度排序，如果相同则按目标语言长度排序。\n",
    "        使用 TokenBatchCreator 逐步组装批量数据，当满足批量大小时返回一个批量的样本信息。\n",
    "        如果不裁剪最后一个批次的数据且存在剩余样本，则将这些样本组成最后一个批次。\n",
    "        如果需要对批量进行洗牌，则对批次进行洗牌操作。\n",
    "        通过迭代器，抛出每个批量的样本在数据集中的索引。\n",
    "        \"\"\"\n",
    "        # 排序，如果源语言长度相同则按照目标语言的长度排列\n",
    "        infos = sorted(self._sample_infos,\n",
    "                       key=lambda x: (x.src_len, x.trg_len))\n",
    "        # 把样本放入到箱子里，所有装箱后的箱子，每一个箱子都放入batch_infos\n",
    "        batch_infos = [] #放箱子的列表，每个箱子都是一个batch\n",
    "        batch_creator = TokenBatchCreator(self._batch_size) # 批量生成器,上面自定义的类\n",
    "        for info in infos:\n",
    "            batch = batch_creator.append(info)\n",
    "            # 存够一个batch的样本信息后，会把这个batch返回，否则返回为None\n",
    "            if batch is not None:\n",
    "                batch_infos.append(batch)\n",
    "\n",
    "        # 是否抛弃最后批量的文本对\n",
    "        if not self._clip_last_batch and len(batch_creator.batch) != 0:\n",
    "            batch_infos.append(batch_creator.batch) # 最后一个batch\n",
    "\n",
    "        # 打乱batch，打乱的是箱子的顺序\n",
    "        if self._shuffle_batch:\n",
    "            self._random.shuffle(batch_infos)\n",
    "\n",
    "        self.batch_number = len(batch_infos) #箱子数量\n",
    "        # print(self.batch_number) #为了理解\n",
    "\n",
    "        # 抛出一个箱子里所有样本的序号\n",
    "        for batch in batch_infos:\n",
    "            batch_indices = [info.i for info in batch] # 批量的样本在数据集中的索引，第一个batch[0,1,.....82]，第二个batch[83,84,85,86,87]\n",
    "            yield batch_indices\n",
    "\n",
    "    def __len__(self):\n",
    "        \"\"\"\n",
    "        返回批量的数量\n",
    "        \"\"\"\n",
    "        if hasattr(self, \"batch_number\"):\n",
    "            return self.batch_number\n",
    "        # 计算批量的数量,没有用到下面的情况，不用看\n",
    "        batch_number = (len(self._dataset) +\n",
    "                        self._batch_size) // self._batch_size\n",
    "        return batch_number"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "d9fcba5b",
   "metadata": {},
   "outputs": [],
   "source": [
    "sampler = TransformerBatchSampler(train_ds, batch_size=4096, shuffle_batch=True)\n",
    "\n",
    "#为什么这里每个批量的样本对数目不一样呢？长度*batch_number>4096的时候，就会返回上一个batch，然后新的样本加入新的batch,具体要看TokenBatchCreator的44行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "5e7a5d82",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第0批量的数据中含有文本对是：[11600, 12766, 16418, 19045]，数量为：4\n",
      "第1批量的数据中含有文本对是：[5475, 7033]，数量为：2\n",
      "第2批量的数据中含有文本对是：[7190, 8625, 8858, 14671, 17524]，数量为：5\n",
      "第3批量的数据中含有文本对是：[3009]，数量为：1\n"
     ]
    }
   ],
   "source": [
    "for idx, batch in enumerate(sampler):\n",
    "    print(\"第{}批量的数据中含有文本对是：{}，数量为：{}\".format(idx, batch, len(batch)))\n",
    "    if idx >= 3:\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a8adf5a",
   "metadata": {},
   "source": [
    "# DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "b1d2c67e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def collate_fct(batch, tokenizer):\n",
    "    src_words = [pair[0].split() for pair in batch]\n",
    "    trg_words = [pair[1].split() for pair in batch]\n",
    "\n",
    "    # [BOS] src [EOS] [PAD]\n",
    "    encoder_inputs, encoder_inputs_mask = tokenizer.encode(\n",
    "        src_words, padding_first=False, add_bos=True, add_eos=True, return_mask=True\n",
    "        )\n",
    "\n",
    "    # [BOS] trg [PAD]\n",
    "    decoder_inputs = tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=True, add_eos=False, return_mask=False,\n",
    "        )\n",
    "\n",
    "    # trg [EOS] [PAD]\n",
    "    decoder_labels, decoder_labels_mask = tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=False, add_eos=True, return_mask=True\n",
    "        )\n",
    "\n",
    "    return {\n",
    "        \"encoder_inputs\": encoder_inputs.to(device=device),\n",
    "        \"encoder_inputs_mask\": encoder_inputs_mask.to(device=device),\n",
    "        \"decoder_inputs\": decoder_inputs.to(device=device),\n",
    "        \"decoder_labels\": decoder_labels.to(device=device),\n",
    "        \"decoder_labels_mask\": decoder_labels_mask.to(device=device),\n",
    "    }\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "17f23bfe",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "encoder_inputs\n",
      "tensor([[   1,   11,  591,   25,   12,    7,   17,  589,    9, 1757,  196,   27,\n",
      "           67,  852, 2352,    4,    3],\n",
      "        [   1,    7,   17,   13,  488,  157,    9,   24,    7, 1242,   73,   12,\n",
      "          198, 1094,  308,    4,    3],\n",
      "        [   1,   29,   58,   12,   11,   25,   97,   14,    8,  926,   12, 1145,\n",
      "          625,    4,    3,    0,    0]])\n",
      "encoder_inputs_mask\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]])\n",
      "decoder_inputs\n",
      "tensor([[  1,   5,  38, 331,  91,  19,   5,  16, 462,  32, 950,  10,  66,   4,\n",
      "           0],\n",
      "        [  1,   5,  16,   6,   5,  45, 157, 109,  69, 113, 364, 265,   5, 757,\n",
      "           4],\n",
      "        [  1,  28, 684,  19,   5,  26,  62,  19, 222,   6,   5, 844,   4,   0,\n",
      "           0]])\n",
      "decoder_labels\n",
      "tensor([[  5,  38, 331,  91,  19,   5,  16, 462,  32, 950,  10,  66,   4,   3,\n",
      "           0],\n",
      "        [  5,  16,   6,   5,  45, 157, 109,  69, 113, 364, 265,   5, 757,   4,\n",
      "           3],\n",
      "        [ 28, 684,  19,   5,  26,  62,  19, 222,   6,   5, 844,   4,   3,   0,\n",
      "           0]])\n",
      "decoder_labels_mask\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]])\n"
     ]
    }
   ],
   "source": [
    "from functools import partial # 固定collate_fct的tokenizer参数\n",
    "\n",
    "#可以调大batch_size,来看最终的bleu，如果GPU内存不够，可以减小batch_size\n",
    "sampler = TransformerBatchSampler(train_ds, batch_size=256, shuffle_batch=True)\n",
    "# https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader\n",
    "sample_dl = DataLoader(train_ds, batch_sampler=sampler, collate_fn=partial(collate_fct, tokenizer=tokenizer)) #partial函数，固定collate_fct的tokenizer参数\n",
    "\n",
    "for batch in sample_dl:#外层是拿每个batch\n",
    "    for key, value in batch.items():#内层是拿每个batch里面是一个字典\n",
    "        print(key)\n",
    "        print(value)\n",
    "    break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "597b9d2c",
   "metadata": {},
   "source": [
    "# 定义模型\n",
    "- Transformer模型由Embedding、Transformer-Block组成\n",
    "- Embedding包括：\n",
    "    - WordEmbedding\n",
    "    - PositionEmbedding\n",
    "- 多头注意力\n",
    "- Transformer-Block包括：\n",
    "    - Self-Attention\n",
    "    - Cross-Attention\n",
    "    - MLP"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "158da269",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(0.0100)\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import math\n",
    "\n",
    "def positional_encoding_weights(emb_size):\n",
    "    # 计算底数和指数\n",
    "    base = math.log(10000)\n",
    "    exponent = -5 / emb_size\n",
    "    \n",
    "    # 应用指数函数\n",
    "    # 将 base 转换为 tensor，因为 torch.exp 需要 tensor 类型的输入\n",
    "    weight = torch.exp(torch.tensor(base)*exponent)\n",
    "    \n",
    "    return weight\n",
    "\n",
    "emb_size = 10  # 举例，可以替换成你需要的大小\n",
    "weight = positional_encoding_weights(emb_size)\n",
    "print(weight)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7335220",
   "metadata": {},
   "source": [
    "## TransformerEmbedding"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "bbb0dfb5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "输入形状: torch.Size([2, 10])\n",
      "输出形状: torch.Size([2, 10, 128])\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import math\n",
    "\n",
    "class TransformerEmbedding(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.vocab_size = config[\"vocab_size\"]\n",
    "        self.hidden_size = config[\"d_model\"]  # 词向量维度\n",
    "        self.pad_idx = config[\"pad_idx\"]\n",
    "        self.max_length = config[\"max_length\"] # 最大token长度\n",
    "        dropout_rate = config[\"dropout\"]  # 随机失活率\n",
    "        \n",
    "        # 词嵌入层\n",
    "        self.word_embedding = nn.Embedding(\n",
    "            num_embeddings=self.vocab_size,\n",
    "            embedding_dim=self.hidden_size,\n",
    "            padding_idx=self.pad_idx\n",
    "        )\n",
    "        \n",
    "        # 位置编码层 - 不需要训练\n",
    "        self.position_embedding = nn.Embedding(\n",
    "            num_embeddings=self.max_length,\n",
    "            embedding_dim=self.hidden_size\n",
    "        )\n",
    "        \n",
    "        # 初始化位置编码，固定不训练\n",
    "        self.register_buffer(\n",
    "            \"position_ids\", torch.arange(self.max_length).expand((1, -1))\n",
    "        )\n",
    "        self.position_embedding.weight.requires_grad = False # 位置编码不需要训练\n",
    "        self._init_position_encoding() # 初始化位置编码\n",
    "        \n",
    "        self.dropout = nn.Dropout(dropout_rate)\n",
    "        \n",
    "    def _init_position_encoding(self):\n",
    "        \"\"\"初始化位置编码权重\"\"\"\n",
    "        position = torch.arange(self.max_length).unsqueeze(1)\n",
    "        div_term = torch.exp(torch.arange(0, self.hidden_size, 2) * (-math.log(10000.0) / self.hidden_size))\n",
    "        \n",
    "        position_encoding = torch.zeros(self.max_length, self.hidden_size)\n",
    "        position_encoding[:, 0::2] = torch.sin(position * div_term)\n",
    "        position_encoding[:, 1::2] = torch.cos(position * div_term)\n",
    "        \n",
    "        self.position_embedding.weight.data.copy_(position_encoding)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        seq_length = x.size(1)\n",
    "        \n",
    "        # 词嵌入\n",
    "        word_embeddings = self.word_embedding(x)\n",
    "        \n",
    "        # 位置编码\n",
    "        position_ids = self.position_ids[:, :seq_length]\n",
    "        position_embeddings = self.position_embedding(position_ids)\n",
    "        \n",
    "        # 词嵌入 + 位置编码\n",
    "        embeddings = word_embeddings + position_embeddings\n",
    "        \n",
    "        # 应用dropout\n",
    "        embeddings = self.dropout(embeddings)\n",
    "        \n",
    "        return embeddings\n",
    "# %% cell 23 code\n",
    "\n",
    "# 初始化TransformerEmbedding并进行前向计算\n",
    "config = {\n",
    "    'vocab_size': 30000,\n",
    "    'd_model': 128,\n",
    "    'max_length': 64,\n",
    "    'pad_idx': 0,\n",
    "    'dropout': 0.1,\n",
    "}\n",
    "\n",
    "# 创建模型实例\n",
    "embedding_model = TransformerEmbedding(config=config)\n",
    "\n",
    "# 创建一个示例输入 (batch_size=2, seq_length=10)\n",
    "sample_input = torch.randint(0, config['vocab_size'], (2, 10))\n",
    "print(f\"输入形状: {sample_input.shape}\")\n",
    "\n",
    "# 前向计算\n",
    "output = embedding_model(sample_input)\n",
    "print(f\"输出形状: {output.shape}\")\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "4e625af9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def plot_position_embedding(position_embedding):# 绘制位置编码\n",
    "    plt.pcolormesh(position_embedding) # 绘制位置编码矩阵\n",
    "    plt.xlabel('Depth')\n",
    "    plt.ylabel('Position')\n",
    "    plt.colorbar() # 颜色条，-1到1的颜色范围\n",
    "    plt.show()\n",
    "\n",
    "# 创建TransformerEmbedding实例并绘制位置编码\n",
    "config = {\n",
    "    'vocab_size': 30000,\n",
    "    'd_model': 128,\n",
    "    'max_length': 64,\n",
    "    'pad_idx':0,\n",
    "    'dropout':0.1,\n",
    "}\n",
    "embedding_model = TransformerEmbedding(config=config)\n",
    "position_embedding_data = embedding_model.position_embedding.weight.data\n",
    "plot_position_embedding(position_embedding_data)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b85c5f6",
   "metadata": {},
   "source": [
    "## MultiHeadAttention(缩放点积注意力)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "99623244",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "\n",
    "\n",
    "Tensor = torch.Tensor\n",
    "@dataclass\n",
    "class AttentionOutput:\n",
    "    hidden_states: Tensor\n",
    "    attn_scores: Tensor\n",
    "\n",
    "class MultiHeadAttention(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.hidden_size = config[\"d_model\"]  # 隐藏层大小\n",
    "        self.num_heads = config[\"num_heads\"]  # 多头注意力的头数\n",
    "        \n",
    "        # 确保hidden_size可以被num_heads整除\n",
    "        assert self.hidden_size % self.num_heads == 0, \"hidden_size must be divisible by num_heads\"\n",
    "        \n",
    "        # 每个头的维度,512/8=64\n",
    "        self.depth = self.hidden_size // self.num_heads\n",
    "        \n",
    "        # 创建四个线性变换层\n",
    "        self.Wq = nn.Linear(self.hidden_size, self.hidden_size)\n",
    "        self.Wk = nn.Linear(self.hidden_size, self.hidden_size)\n",
    "        self.Wv = nn.Linear(self.hidden_size, self.hidden_size)\n",
    "        self.Wo = nn.Linear(self.hidden_size, self.hidden_size)\n",
    "        \n",
    "    def split_heads(self, x, batch_size):\n",
    "        \"\"\"\n",
    "        将输入张量分割成多个头\n",
    "        x shape: (batch_size, seq_len, hidden_size)\n",
    "        返回 shape: (batch_size, num_heads, seq_len, depth)\n",
    "        \"\"\"\n",
    "        # 重塑为 (batch_size, seq_len, num_heads, depth)\n",
    "        x = x.view(batch_size, -1, self.num_heads, self.depth)\n",
    "        # 转置为 (batch_size, num_heads, seq_len, depth)\n",
    "        return x.permute(0, 2, 1, 3)\n",
    "    \n",
    "    def merge_heads(self, x, batch_size):\n",
    "        \"\"\"\n",
    "        将多头注意力的输出合并\n",
    "        x shape: (batch_size, num_heads, seq_len, depth)\n",
    "        返回 shape: (batch_size, seq_len, hidden_size)\n",
    "        \"\"\"\n",
    "        # (batch_size, num_heads, seq_len, depth) 转置回 (batch_size, seq_len, num_heads, depth)\n",
    "        x = x.permute(0, 2, 1, 3)\n",
    "        # 重塑为 (batch_size, seq_len, hidden_size)\n",
    "        return x.reshape(batch_size, -1, self.hidden_size)\n",
    "    \n",
    "    def scaled_dot_product_attention(self, Q, K, V, mask=None):\n",
    "        \"\"\"\n",
    "        计算缩放点积注意力\n",
    "        Q, K, V shape: (batch_size, num_heads, seq_len, depth)\n",
    "        mask shape: (batch_size, 1, 1, seq_len) 或 (batch_size, 1, seq_len, seq_len)\n",
    "        \"\"\"\n",
    "        # 计算注意力分数\n",
    "        matmul_qk = torch.matmul(Q, K.transpose(-2, -1))  # (batch_size, num_heads, seq_len_q, seq_len_k)\n",
    "        \n",
    "        # 缩放\n",
    "        dk = torch.tensor(self.depth, dtype=torch.float32)\n",
    "        scaled_attention_logits = matmul_qk / torch.sqrt(dk)\n",
    "        print(f'scaled_attention_logits.shape:{scaled_attention_logits.shape}')\n",
    "        print(f'mask.shape{mask.shape}')\n",
    "        # 应用掩码（如果提供）\n",
    "        if mask is not None:\n",
    "            scaled_attention_logits += (mask * -1e9)\n",
    "        \n",
    "        # softmax归一化\n",
    "        attention_weights = F.softmax(scaled_attention_logits, dim=-1)  # (batch_size, num_heads, seq_len_q, seq_len_k)\n",
    "        \n",
    "        # 应用注意力权重\n",
    "        #(batch_size, num_heads, seq_len_q, seq_len_k) * (batch_size, num_heads, seq_len_v, depth) = (batch_size, num_heads, seq_len_q, depth)\n",
    "        output = torch.matmul(attention_weights, V)  # (batch_size, num_heads, seq_len_q, depth)\n",
    "        \n",
    "        return output, attention_weights\n",
    "    \n",
    "    def forward(self, Q, K, V, mask=None):\n",
    "        \"\"\"\n",
    "        入参Q,K,V都是X\n",
    "        \"\"\"\n",
    "        batch_size = Q.shape[0]\n",
    "        \n",
    "        # 线性变换\n",
    "        Q = self.Wq(Q)  # (batch_size, seq_len_q, hidden_size)\n",
    "        K = self.Wk(K)  # (batch_size, seq_len_k, hidden_size)\n",
    "        V = self.Wv(V)  # (batch_size, seq_len_v, hidden_size)\n",
    "        \n",
    "        # 分割成多头\n",
    "        Q = self.split_heads(Q, batch_size)  # (batch_size, num_heads, seq_len_q, depth)\n",
    "        K = self.split_heads(K, batch_size)  # (batch_size, num_heads, seq_len_k, depth)\n",
    "        V = self.split_heads(V, batch_size)  # (batch_size, num_heads, seq_len_v, depth)\n",
    "        \n",
    "        # 计算缩放点积注意力,scaled_attention (batch_size, num_heads, seq_len_q, depth)\n",
    "        # attention_weights (batch_size, num_heads, seq_len_q, seq_len_k)\n",
    "        scaled_attention, attention_weights = self.scaled_dot_product_attention(Q, K, V, mask)\n",
    "        \n",
    "        # 合并多头\n",
    "        concat_attention = self.merge_heads(scaled_attention, batch_size)  # (batch_size, seq_len_q, hidden_size)\n",
    "        \n",
    "        # 最后的线性层\n",
    "        output = self.Wo(concat_attention)  # (batch_size, seq_len_q, hidden_size)\n",
    "        \n",
    "        return AttentionOutput(hidden_states=output, attn_scores=attention_weights)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "970f1a70",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "输入张量形状: torch.Size([2, 4, 8])\n",
      "输出张量形状: torch.Size([2, 4, 8])\n",
      "注意力权重形状: torch.Size([2, 2, 4, 4])\n"
     ]
    }
   ],
   "source": [
    "# 验证MultiHeadAttention的正向计算\n",
    "import torch\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 创建一个简单的测试用例\n",
    "batch_size = 2\n",
    "seq_len = 4\n",
    "hidden_size = 8\n",
    "num_heads = 2\n",
    "\n",
    "# 创建输入张量\n",
    "x = torch.randn(batch_size, seq_len, hidden_size)\n",
    "print(f\"输入张量形状: {x.shape}\")\n",
    "\n",
    "# 创建配置字典\n",
    "config = {\n",
    "    'd_model': hidden_size,\n",
    "    'num_heads': num_heads\n",
    "}\n",
    "\n",
    "# 创建MultiHeadAttention实例\n",
    "mha = MultiHeadAttention(config)\n",
    "\n",
    "# 执行前向传播 (Q=K=V=x，无掩码)\n",
    "output = mha(x, x, x)\n",
    "\n",
    "# 打印结果\n",
    "print(f\"输出张量形状: {output.hidden_states.shape}\")\n",
    "print(f\"注意力权重形状: {output.attn_scores.shape}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2f28a04",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "查询张量形状: torch.Size([2, 4, 8])\n",
      "键值张量形状: torch.Size([2, 6, 8])\n",
      "输出张量形状: torch.Size([2, 4, 8])\n",
      "注意力权重形状: torch.Size([2, 2, 4, 6])\n"
     ]
    }
   ],
   "source": [
    "# 验证MultiHeadAttention做交叉注意力正向计算的没问题的\n",
    "import torch\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 创建一个简单的测试用例\n",
    "batch_size = 2\n",
    "seq_len_q = 4\n",
    "seq_len_kv = 6  # 不同的序列长度\n",
    "hidden_size = 8\n",
    "num_heads = 2\n",
    "\n",
    "# 创建输入张量\n",
    "q = torch.randn(batch_size, seq_len_q, hidden_size)\n",
    "kv = torch.randn(batch_size, seq_len_kv, hidden_size)\n",
    "print(f\"查询张量形状: {q.shape}\")\n",
    "print(f\"键值张量形状: {kv.shape}\")\n",
    "\n",
    "# 创建配置字典\n",
    "config = {\n",
    "    'd_model': hidden_size,\n",
    "    'num_heads': num_heads\n",
    "}\n",
    "\n",
    "# 创建MultiHeadAttention实例\n",
    "mha = MultiHeadAttention(config)\n",
    "\n",
    "# 执行前向传播 (Q=q, K=V=kv，无掩码)\n",
    "output = mha(q, kv, kv)\n",
    "\n",
    "# 打印结果\n",
    "print(f\"输出张量形状: {output.hidden_states.shape}\")\n",
    "print(f\"注意力权重形状: {output.attn_scores.shape}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26e43424",
   "metadata": {},
   "source": [
    "## TransformerBlock"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "66324f21",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "from typing import Optional\n",
    "\n",
    "@dataclass\n",
    "class TransformerBlockOutput:\n",
    "    hidden_states: torch.Tensor\n",
    "    self_attn_scores: torch.Tensor\n",
    "    cross_attn_scores: Optional[torch.Tensor] = None\n",
    "\n",
    "class TransformerBlock(nn.Module):\n",
    "    def __init__(self, config, has_cross_attention=False):\n",
    "        super().__init__()\n",
    "        self.has_cross_attention = has_cross_attention\n",
    "        self.d_model = config['d_model']\n",
    "        self.ffn_dim = config.get('dim_feedforward', 4 * self.d_model)  # 使用配置中的ffn_dim或默认值\n",
    "        \n",
    "        # 为不同组件创建独立的dropout层\n",
    "        self.self_attn_dropout = nn.Dropout(config.get('dropout', 0.1))\n",
    "        self.cross_attn_dropout = nn.Dropout(config.get('dropout', 0.1))\n",
    "        self.ffn_dropout = nn.Dropout(config.get('dropout', 0.1))\n",
    "        \n",
    "        # 自注意力层\n",
    "        self.self_attention = MultiHeadAttention(config)\n",
    "        # 层归一化\n",
    "        self.self_attn_layer_norm = nn.LayerNorm(self.d_model)\n",
    "        \n",
    "        # 交叉注意力层（可选）\n",
    "        if has_cross_attention:\n",
    "            self.cross_attention = MultiHeadAttention(config)\n",
    "            self.cross_attn_layer_norm = nn.LayerNorm(self.d_model)\n",
    "        \n",
    "        # 前馈神经网络\n",
    "        self.ffn = nn.Sequential(\n",
    "            nn.Linear(self.d_model, self.ffn_dim),\n",
    "            nn.ReLU(),\n",
    "            # nn.Dropout(config.get('dropout', 0.1)),  # FFN内部的dropout\n",
    "            nn.Linear(self.ffn_dim, self.d_model)\n",
    "        )\n",
    "        self.ffn_layer_norm = nn.LayerNorm(self.d_model)\n",
    "    \n",
    "    def forward(self, x, encoder_output=None, self_attn_mask=None, cross_attn_mask=None):\n",
    "        # 自注意力层 + dropout + 残差连接 + LayerNorm\n",
    "        self_attn_output = self.self_attention(x, x, x, self_attn_mask)\n",
    "        x = x + self.self_attn_dropout(self_attn_output.hidden_states)  # 使用专用的self_attn_dropout\n",
    "        x = self.self_attn_layer_norm(x)\n",
    "        \n",
    "        self_attn_scores = self_attn_output.attn_scores\n",
    "        cross_attn_scores = None\n",
    "        \n",
    "        # 交叉注意力层（如果有） + dropout + 残差连接 + LayerNorm\n",
    "        if self.has_cross_attention and encoder_output is not None:\n",
    "            cross_attn_output = self.cross_attention(x, encoder_output, encoder_output, cross_attn_mask)\n",
    "            x = x + self.cross_attn_dropout(cross_attn_output.hidden_states)  # 使用专用的cross_attn_dropout\n",
    "            x = self.cross_attn_layer_norm(x)\n",
    "            cross_attn_scores = cross_attn_output.attn_scores\n",
    "        \n",
    "        # 前馈神经网络 + dropout + 残差连接 + LayerNorm\n",
    "        ffn_output = self.ffn(x)\n",
    "        x = x + self.ffn_dropout(ffn_output)  # 使用专用的ffn_dropout\n",
    "        x = self.ffn_layer_norm(x)\n",
    "        \n",
    "        return TransformerBlockOutput(\n",
    "            hidden_states=x,\n",
    "            self_attn_scores=self_attn_scores,\n",
    "            cross_attn_scores=cross_attn_scores\n",
    "        )\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36a5a199",
   "metadata": {},
   "source": [
    "## TransformerEncoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "081570d2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "from typing import List\n",
    "\n",
    "import torch\n",
    "from torch import Tensor\n",
    "import torch.nn as nn\n",
    "\n",
    "@dataclass\n",
    "class TransformerEncoderOutput:\n",
    "    last_hidden_states: Tensor\n",
    "    attn_scores: List[Tensor]\n",
    "\n",
    "class TransformerEncoder(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.num_layers = config.get('encoder_layers', 6)\n",
    "        \n",
    "        # 创建多层Transformer编码器块\n",
    "        self.layers = nn.ModuleList([\n",
    "            TransformerBlock(config, has_cross_attention=False)\n",
    "            for _ in range(self.num_layers)\n",
    "        ])\n",
    "    \n",
    "    def forward(self, x, attention_mask=None):\n",
    "        # 用于收集所有层的注意力分数\n",
    "        all_attn_scores = []\n",
    "        \n",
    "        # 依次通过每个编码器层\n",
    "        for layer in self.layers:\n",
    "            output = layer(x, self_attn_mask=attention_mask)\n",
    "            x = output.hidden_states\n",
    "            all_attn_scores.append(output.self_attn_scores)\n",
    "        \n",
    "        return TransformerEncoderOutput(\n",
    "            last_hidden_states=x,\n",
    "            attn_scores=all_attn_scores\n",
    "        )\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "abe12b93",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]],\n",
      "\n",
      "\n",
      "        [[[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]]]])\n",
      "scaled_attention_logits.shape:torch.Size([2, 4, 12, 12])\n",
      "mask.shapetorch.Size([2, 1, 1, 12])\n",
      "scaled_attention_logits.shape:torch.Size([2, 4, 12, 12])\n",
      "mask.shapetorch.Size([2, 1, 1, 12])\n",
      "输出张量形状: torch.Size([2, 12, 64])\n",
      "注意力层数: 2\n",
      "第一层注意力分数形状: torch.Size([2, 4, 12, 12])\n",
      "第一层第二个样本的注意力分数，第9个位置是否接近0: False\n"
     ]
    }
   ],
   "source": [
    "def test_transformer_encoder():\n",
    "    # 创建一个简单的配置\n",
    "    config = {\n",
    "        'encoder_layers': 2,\n",
    "        'd_model': 64,\n",
    "        'num_heads': 4,\n",
    "        'dim_feedforward': 128,\n",
    "        'dropout': 0.1,\n",
    "        'max_length': 100,\n",
    "        'vocab_size': vocab_size,\n",
    "        'pad_idx': 0,\n",
    "    }\n",
    "    \n",
    "    # 实例化模型\n",
    "    encoder = TransformerEncoder(config)\n",
    "    \n",
    "    # 生成一个批次的输入\n",
    "    batch_size = 2\n",
    "    hidden_size = config['d_model']\n",
    "    \n",
    "    inputs_words = [\"The quick brown fox jumps over the lazy dog .\", \"What does the fox say ?\"]\n",
    "\n",
    "    #encode变为id\n",
    "    # 使用tokenizer将文本编码为token IDs\n",
    "    src_sequence, src_padding_mask = tokenizer.encode([w.split() for w in inputs_words], return_mask=True)\n",
    "    # 通过TransformerEmbedding处理输入序列\n",
    "    src_embedded = TransformerEmbedding(config)(src_sequence)\n",
    "    print(src_padding_mask.unsqueeze(1).unsqueeze(2))\n",
    "    \n",
    "    # 执行前向传播\n",
    "    output = encoder(src_embedded, src_padding_mask.unsqueeze(1).unsqueeze(2))\n",
    "    \n",
    "    # 验证输出形状\n",
    "    print(f\"输出张量形状: {output.last_hidden_states.shape}\")\n",
    "    print(f\"注意力层数: {len(output.attn_scores)}\")\n",
    "    print(f\"第一层注意力分数形状: {output.attn_scores[0].shape}\")\n",
    "    \n",
    "    # 检查注意力掩码是否正常工作\n",
    "    # 对于被掩盖的位置，注意力分数应该很小\n",
    "    print(f\"第一层第二个样本的注意力分数，第9个位置是否接近0: {output.attn_scores[0][1, :, 8:].mean().item() < 0.01}\")\n",
    "    \n",
    "    return output\n",
    "\n",
    "# 运行测试\n",
    "test_output = test_transformer_encoder()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97a1b24f",
   "metadata": {},
   "source": [
    "## TransformerDecoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "eebcf9f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "from typing import List\n",
    "\n",
    "@dataclass\n",
    "class TransformerDecoderOutput:\n",
    "    last_hidden_states: torch.Tensor\n",
    "    self_attn_scores: List[torch.Tensor]\n",
    "    cross_attn_scores: List[torch.Tensor]\n",
    "\n",
    "class TransformerDecoder(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        self.layers = nn.ModuleList([\n",
    "            TransformerBlock(\n",
    "                config,\n",
    "                has_cross_attention=True\n",
    "            )\n",
    "            for _ in range(config['num_decoder_layers'])\n",
    "        ])\n",
    "        self.dropout = nn.Dropout(config['dropout'])\n",
    "        \n",
    "    def forward(self, x, encoder_output, self_attn_mask=None, cross_attn_mask=None):\n",
    "        self_attn_scores = [] # 存储每个层的自注意力分数\n",
    "        cross_attn_scores = [] # 存储每个层的交叉注意力分数\n",
    "        \n",
    "        for layer in self.layers:\n",
    "            layer_output = layer(\n",
    "                x, \n",
    "                encoder_output, \n",
    "                self_attn_mask, \n",
    "                cross_attn_mask\n",
    "            )\n",
    "            \n",
    "            x = layer_output.hidden_states\n",
    "            self_attn_scores.append(layer_output.self_attn_scores)\n",
    "            cross_attn_scores.append(layer_output.cross_attn_scores)\n",
    "        \n",
    "        return TransformerDecoderOutput(\n",
    "            last_hidden_states=x,\n",
    "            self_attn_scores=self_attn_scores,\n",
    "            cross_attn_scores=cross_attn_scores\n",
    "        )\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1e476f2",
   "metadata": {},
   "source": [
    "## 生成look ahead mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "id": "8254102c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[False,  True,  True,  True,  True],\n",
       "        [False, False,  True,  True,  True],\n",
       "        [False, False, False,  True,  True],\n",
       "        [False, False, False, False,  True],\n",
       "        [False, False, False, False, False]])"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(torch.triu(torch.ones(5, 5))==0).transpose(-1, -2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "1db53d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Look-ahead mask for size 3:\n",
      "tensor([[False,  True,  True],\n",
      "        [False, False,  True],\n",
      "        [False, False, False]])\n",
      "\n",
      "Look-ahead mask for size 5:\n",
      "tensor([[False,  True,  True,  True,  True],\n",
      "        [False, False,  True,  True,  True],\n",
      "        [False, False, False,  True,  True],\n",
      "        [False, False, False, False,  True],\n",
      "        [False, False, False, False, False]])\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAbEAAAGZCAYAAAAHLw/qAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAPZxJREFUeJzt3Q2cTPX+B/DvLHbX89PmYVmRRB7X0wop5WHrulLqhoRLkSK0EluxSiwllyKipG6JbiFJJBHCXQ/pcvNYWE+LvdWuh2uXnfN/fX63mf/M7OzuzM6xc86Zz/u+zs2cnTnzm5kz8z2/3+/7+/1smqZpQkREZEJhwS4AERFRYTGIERGRaTGIERGRaTGIERGRaTGIERGRaTGIERGRaTGIERGRaTGIERGRaTGIERGRaTGIUdAsWrRIbDab7Ny5s8ies2PHjtK4ceMC73fs2DFVNpTRYeLEiWof0fXiOMfS09ODXRTTMHUQu3jxoiQlJck999wjlSpVyvWjQ2R0WVlZMnbsWImOjpaSJUtKmzZtZN26dcEuFpFpmDqI4Wrl5Zdflv3790uzZs2CXRyykBtvvFH++9//Sr9+/a7r8/z1r3+VGTNmSN++fWXWrFlSrFgx+dOf/iRbtmy5rs9LZBXFxcSqV68uZ86ckWrVqqkmqdatWwe7SGQRqNVHRkZe1+dISUmRJUuWyGuvvSbPPvus2te/f3/V3Pncc8/J1q1br+vzE1mBqWtiERERKoAFchVcpkwZSU1NlT//+c/q3zVq1JA5c+aov+/du1fuvvtuKV26tLoyX7x4ca5j/P777zJq1CiJiYlR5bn55ptl2rRpYrfb3e43ffp0adeunVSuXFk1G7Vs2VI+/fRTrz+ew4cPlxUrVqgfMxyzUaNGsmbNGrf7XbhwQT1v7dq11X2qVKkiXbp0kd27d/vU5n7o0CF59NFHpXz58nLDDTfI+PHjBQsanDhxQnr06CHlypVT7+3rr7/u9vjs7GyZMGGCKj8ei/emQ4cOsmHDhlzPhR9o3K9s2bLqeE2aNFG1jfz89ttvEhcXJzVr1pSDBw96vQ/ec9RY3njjDbdaeVhYmHp/XRdmePLJJ72eIz/99JPcddddUqpUKfWZv/rqqwX2ieXlww8/VK8TnyuatXv37q3ex4Lg88frGDJkiHMfAudjjz0m27ZtK/AYjv69f/3rX3LnnXeq14Lzz3Fefffdd6p5EuWqX7++fPPNN7mOcerUKRk0aJBUrVrVea4tXLiwUJ+54z3DuT5//nypW7euOiYuLnfs2OF237S0NBk4cKD6nHEfXJDivMMxCnrN2Lx9l/FdKExZAv0d+PXXX9VFCM5vPBbn+r333is//vhjrnK++eab6j3GZ1WxYkVp1aqV198VV8ePH1efKz7rs2fPilFt2rRJunfvrprG8d7jN6wgGzdulBYtWjh/OwvTHWTqIKaHnJwcdcIhCOGHDF8EBBG8mehrw0mGoIQfYlwlHz161PnYy5cvqx8P/Ijhb/hRbd++vSQmJkpCQoLb8+DHu3nz5qr5c8qUKVK8eHH5y1/+Il9++WWuMqEp6amnnlI/hijTlStX5MEHH5T//Oc/zvsMHTpU5s6dq/a/9dZb6kuEHys0rfqiV69eKtBOnTpV/dC98sorMnPmTBUI8QXGa8ZJhePi5HTIzMyUd955R/2Q4D4IiufPn5f4+HjZs2eP837o1+nTp4/6ouJ+eB485vvvv8+zTAhE+LHAFxU/wPjh9aZChQrqC+1aLrxn+OLgBwUBymHz5s3qB9czUOKzRRM0gnSDBg1Uv9RXX30l/po8ebL67OvVq6eaBXFhsX79ernjjjtUsM3PDz/8ILfccov60XOFIA6u72de8Frww4vPEOcKfgxw3ixdulT9F02TeO8vXbokDz30kLr4ccD7fNttt6nghnMe5yg+cwRRnAv+fuYO+FFG7fKJJ55Q5xUCSs+ePeXq1avO++C8Xb58uQpkOH9HjBihyoZAoidfyhLo78Avv/yifrDxOeAcGDNmjAp8+G04ffq0834LFixQr7Nhw4bq/X3ppZckNjZW/vnPf+ZZ/p9//lmdS3he/ODjYsOoLl26pL5TjuBfELyH3bp1UxeTOI/w3Xn88cdl7dq1/j2xZhE7duzA5bf23nvv+fyYAQMGqMdMmTLFue+3337TSpYsqdlsNm3JkiXO/QcOHFD3TUpKcu6bNGmSVrp0ae3QoUNuxx03bpxWrFgxLTU11bnv8uXLbvfJzs7WGjdurN19991u+/Ec4eHh2pEjR5z7fvzxR7X/zTffdO4rX768NmzYMM1fKD+ONWTIEOe+a9euaTVr1lSveerUqbneC7xPrvfNyspyOybuV7VqVW3QoEHOfSNHjtTKlSun7p8XfFYoCz67M2fOaI0aNdJuuukm7dixYwW+Drx2PKdDQkKCdscdd2hVqlTR5s6dq/b95z//Ua9p1qxZzvvdeeed6jk/+OAD5z68nmrVqmkPPvigc9/Ro0dznU+O984B5cTnPHnyZLey7d27VytevHiu/Z7wej0/f/j3v/+tnmfevHn5Pt7xWhYvXpzrPA0LC9O2b9/u3L927dpcr+exxx7TqlevrqWnp7sdt3fv3ur8cpyzvn7mjvescuXK2q+//urc//nnn6v9X3zxhfOxuP3aa6/l+/ryes3YPOEcvfHGG/0uix6/A1euXNFycnLcyoPnj4iI0F5++WXnvh49eqjPPD+Oc+z8+fPa/v37tejoaK1169Zur8EMRERbvnx5vvd57rnncr0fvXr10uLj4/16rpCviQGiv+tVPmoAaDp4+OGHnfuxD3/DVZfDP/7xD3WVj9oGahGOrXPnzurKzrWmgFqS69VzRkaGeqy35j88Hs0fDk2bNlVX667PjbLgCs71Sq+wrxlNWrjSxLmHq3DP98L1eXHf8PBw9W/U5FDzuXbtmnq862vBY3Fl5kum3cmTJ9VVK66O8Z6hyaYgeO9Qk3A0OaLGhStW7Me/HbUzvCbPmhiafNCU6oDXg9qP6+v0xbJly9R7gPPE9fNH8yVqZt6aWF0hcQQ1J0+Ovjj8vSB4LahxeZ6nt956q6qdOTj+7XiNeF8+++wz1fyDf7uWHzUsnJ+Oz9PXz9y1lo/vhIPj/Xc8N74LOB5qFvguXE8FlUWP3wF8hmjKBnzv0WKCzwX39fxO4Fz3bM70Zt++feo7gRohasqur8Eqtm3bpn7rXOHcw/6QSezQA34w0CfkCu3+aKv3HBOE/a5fusOHD6v+CM/HO5w7d87571WrVqnmDFSbkVbt4G3cUa1atXLtw0ns+txo8hgwYIBq/kBfBZqN0Mxx0003+fS6PZ8Drw3vRVRUVK79rs2Y8P7776tmuAMHDrg1y9SpU8f5bzSHfvLJJ6qJBs2TXbt2VT8GaJrxhAxANK+iKdTXPk7HjxECFj4rNM3h/cVngX4Qx98Q/D0zV719tnh/8Vn6A58/AgACljclSpTI9/H4MXc9FxzQfOz4e0HyOk9xXnjuA8c5hOZANHeivwhbQeevL595XueW4wfY8dz40UfT3OjRo1XzGJo00RSH8zeQPm5vCiqLHr8DCOxoikWzKJrIEMgc0EfrgCZrBCRcMKHZFt+JRx55RHVBeMLFBd4bNK0hIPrjypUrqh9TDzi/PV8/Pj9vF1/+Qr+oZ/MobqP5Ghdwvpz/EPJBDFeZ/ux3TRrAyYs+JGSSeYP+DseP6X333adqCjjR0YmNH7j33nvPa6euL8+NgIAfcvQrfP3116rdHz8MqB0gcBTE23P48rzo/0NH+P3336/a/pFQgsclJyer9nsH7EfAxpcQfU3Y8HrxQ4UfRFfoo/jggw/UDwGO4wt0HuMHFDU3XK2ijG3btlU/RCNHjlSd4XjfkUzjuEr253X6Ap8/vuB4bd6OWdCPD84DJFZ4Qsat4zVer/PXkXiEGikuhrxBC4A/n7mvzw3o/8APNfqScI4gsQjH+/bbb1XfcV7wfnv7nFwDh79lye9+vjwefdwoPxJkJk2apJJ7cM7hNbomeKF2jJYDXNAiUQs1YfweIGkG/WOu0GeI78lHH32k+vP8CWB1biwjaee8vx/+wjmM8biuMDYX/aJGEfJBLBBo8sMH7Fkl9oSTFVd6+LK6XsHgRz0Q+BFEjQcbrpqR5YNEA1+CWGEh8w21PQRL1ys0nNie0GSEHyps+DKjnG+//bb6wuNK1OHpp59Wt/FlxlXuuHHjfCoLgjiCGIIZOsjR+Y1aF46BHwk05Xj+OOj9+ePHDM/vuGDxB8qMJkdcebomdzg6+vH36wXBHu8XfvwLOn/9+cz9ff9QG8OGWi1eL2p7CJp5QU3KW1MgLlqCBe8PkhPeffddt/2o6Xq2bKB5Ek2c2FBbwgUcvrNIBnMd0oGLUrRO4DuDzwk1Nl9kZ2erAHZ0141SrmxgvUWZF+xSp+VxlSXren7qUQsD1Lo9sy1xG8/lay0M2CcWANSG0H7rLZsGJzD6DRxXc/jyu14tIkvKlxRUb3Ac9Fm4wtUxrty9NU/pyXFl6nolih9dz3ZszyZIXJk6ruy9lRGBDZmQ+DIj69LXIIb3EZl4juZFPA9qX8gSQ7OXZ3+YnvADhPcDgdLzyh63Pd8DT8gWxGfp2pyH9wYXN+jD8mwS1BPKjat9XGCh/8UTmhtd7+vLZ+4rZPU6mkxdAxp+rAs6f3E/NGm6lg+p7PllvV5veH88P3/0l3vWsj3PB1zkIVMRj/XMlsTvBc4LnCOoKa9cudKvMpUuo88GCCqum15BDC0nyOR1hT507A+pmtjs2bNVwHAkOHzxxReq89Rxhe/oC7ge0LSCkwvt+WhuQd8UkhmQXourM/zA4koMaaT4UUV/EK6oUGtCGipqH/72wwBSkdFWjxMcNQ9U+dHWjg5jz3FdesNrxRX5Aw88oF4X+gDmzZunvoyuzQ7oJEcCAFLmUVZcKWOMDK620aziDa4+EZyHDRumftBcky+8cQQoNNGgSccBzbZo4nOMC7pe8IOKfjgEXnzWaG5DufGeoJkX478cg5i9QaDCMAs8HucEzgc0IeFYnlf11wNS71ETRDkGDx6sPkN8ZqjB4nzCv/35zH2FMYqdOnVSF4E4BmoceL9wFe6apOINmuzwXUICAJKQ8L6hLBh7hRptMOD9wdAZDBfABRS+/2gG9OyfRh8Yah/oA0PfD/qA8fuF9xTnjSdckKFWivMK79Xq1avV98moLl68KEeOHHHexnmCLgU0r6JvEuc5Aju6DhzDhPD60R2DzxVNyehH9zbsKF+aySGtFi/D24Y01/wgtRYp8p6QwustFRbP1a1bN7d9Fy5c0BITE7Wbb75ZpcZHRUVp7dq106ZPn67S6B3effddrV69eirttkGDBirV2TNlG3DbW+o8ntuR6o505zFjxmjNmjXTypYtq14D/v3WW28V+H65pvAW5r2w2+0qFRnlwWtp3ry5tmrVqlwpzp9++qnWtWtXlfKO96VWrVraE088oVLpvaXYOyBVuU+fPipFfcWKFQW+Hhwfxzh79qxz35YtW9S+Dh06FPh6CkrRzi/F3uGzzz7Tbr/9dvX+YcPni8/w4MGDBZb/v//9r/bss8+qFH+8n0inXrNmTYGP8/c8zevcwvuGfTExMVqJEiVUOTp16qTNnz/f78/c8Z55S513TUtHSj+eE+8T3i+k87dp00b75JNPfHrdH374oRqKgfMqNjZWDR8obFn0+B1Aiv3o0aPVcAWk5bdv317btm1bruEAb7/9thoGgrR/vI9169ZV3+OMjIx8v58Y6oDjlClTxm3YhDcZGRnq8WkHa2mXT9cOaMMxcCzX8uVnw4YNXn+HHb9b+K/n8Ag8Bp8hPkt8pv4MkXKw4f/0jshERFT0MjMzVevT6YM1dekTi65/UrWOeA7INxL2iRERkWmZvk+MiIjc5Wia2gIR6OOLCoMYEZHF2EVTW6DHMAM2JxIRkWmxJkZEZDF20SQnRGpiDGJERBZjZ3MiERGR8bEmRkRkMTkhlJ3ImpiImgIKM6FjAk5MwZOSkhK0smAmb0yVhGloMB8ippxxrJllFJiuCHO7YZbuYMIUNpiaCstdYMJQLA+/c+fOoJYJcyFiHkhMCowyYWoqzGxelHMKFLRMPMqCyZYxgTTKiAmAMQFvsMqEeQOxTAk+P0yQi/tgtYPCrpWnR5k8YYok3Md1xWsjs+u0mUHIBzFMHpuQkKBm5MaccZiLEPOyua6lVJS+++47NXfg9u3b1WSY+IJjzjXMyWgEmJ8RM9E7JvMNFqznhDnosKQN5kn86aef1LyRwV48EMvhYAJjzAmHufFwG2u/Yd5IoywTj/K88cYbas5BTOSLwIFz3nNS3qIqEyYExncPwR//xTyNuHDD8kXBfJ8cMK8jvo++LI1jFDl/JHYEupmCFuLi4uLc5pPD3H1YEjw5OVkzgnPnzqn5x7777rtgF0XNE4n5H9etW6fmQBs5cmTQyjJ27Fg1X6HRYE69QYMGue3r2bOn1rdvX0MsE495EDE/ouucgr///ruay+/jjz8OSpm8SUlJUfc7fvx4UMt08uRJrUaNGtq+ffvUnIl/+9vfNCPL+GPuxH/vr6KlnqwW0IZj+DN3YrCEdE0Ma+/s2rXLbT0lzByN24VdZkJvjiVXMBN0sKGGiBm3C1p/qihg9YBWrVqpWeDR7IqFFBcsWBDsYqlZzLG8BGZqdywTsmXLluu6xps/MLM4VtR1/Qwx1x6a0Y1yzjvOezTfVahQIWhlwBp4WHUcq1VglnwzydH02cwgpBM70tPTVR+GtyWysWZRsOFLhH4nNJs1btw4qGVZsmSJaupBc6IRYGFENNuhKfj5559X5RoxYoRaoymvlYqLAhb0xCSsDRo0UOtM4fzCood9+/YVI0AAA2/nvONvwYZmTfSR9enTJ6gTz6IpGMvE4LwyG7sOfVpm6RML6SBmdKj5YMFCXMkHE1Z2HTlypOqjc119NtgBHjUxxzpiqInhvUI/TzCDGNZDwlpSixcvVlfvWE8JFyLoTwlmucwCfcBYOwstfL4ujno9oIVm1qxZ6sLNdTVrMp6Qbk7EgpW4Wva2RDYWrwum4cOHy6pVq9SihVhUMpjwhUaiS4sWLdSVKTYkoCA5AP92XbG6qCCzDgsqusJim6mpqRJMaHpCbQyLOyLbDs1RzzzzjMo6NQLHeW3Ec94RwLCAKi6YglkL27x5szrnsZij45xHuUaPHq0ymY3OLjbJCXDDMcwgpIMYmp6wGrPrEtm4wsdtf5fI1guuQBHAkBGFlU6Rqh1sWIUXq9WiVuHYUAtCExn+7Vi+viihidVz6AH6oW688UYJJmTaoV/VFd4fnFdGgPMJwcr1nEfzJ7IUg3XOuwYwpPpjVWkMmwgmXHxg1XXXcx61aVykrF27VozOrumzmUHINyeiTwXNPPhRjouLU+NAkHqLpcaD1YSIpqjPP/9cjRVz9FOg8x1jeoIB5fDsk0NaNn5ogtVXh9oNkijQnIgfP4ztmz9/vtqCCeOO0AeGK3g0J/7www8yY8YMtfy6UZaJR/PmK6+8IvXq1VNBDant+IHGmMRglAm16oceekg13aH1ATV7x3mPv+NiMxjvk2cgxXAOXADUr1//upSHCinY6ZFG8Oabb2q1atVSS2Qj5b6gJcCvJ2/Le2MrzLLd11OwU+zhiy++0Bo3bqzSw7HU/fz587Vgy8zMVO8LzqfIyEi15PoLL7ygZWVlFVkZClomHmn248eP16pWrareu06dOmkHDx4MWpmOHj2a53mPxwWjTN6YKcX+n/+upv07NTqgDccwQ4q9Df9X2ABIRETGkZmZqVpttv67upQpG1hv0cULdmnX6Iwa7hDM/smChHSfGBERmVvI94kREVmNXbOpLdBjmAGDGBGRxeT8kSYf6DHMgM2JRERkWqyJERFZTI6EqS2wY5gDgxgRkcVoOvSJ4RhmwCBGRGQxOewTCy1ZWVkyceJE9V+jMGKZjFoulsk3LJP5y0W5cbCzywBBIw3qM2KZjFoulsk3LJP5y+Vrub/6Vx0pHeBg50sX7HJv06OGfw/YnEhEZDF2NQt9YEHMrmbhMj42JxIRkWlZviaGJTBOnz6tZmLPa3E7VMFd/2sERiyTUcvFMvmGZTJuudCrc+HCBbWagOdSPoWRE0KJHZbvEzt58qTExMQEuxhERD6toh7IIriZf/SJLf+xnpQuG9g6f5cu5MgDzQ6zTyzYUAOD2+VPUlxKBHy85Yf26lAqIqL/l3nRLje2OOb8vSLfWT6IOZoQEcCK2wIPYuUCzPghIspLXl0ehUvssAV8DDOwfBAjIgo1dh2mnWJ2IhER0XVmiiA2Z84cqV27tkRGRkqbNm0kJSUl2EUiIjKsHC1Ml80MDF/KpUuXSkJCgiQlJcnu3bulWbNmEh8fL+fOnQt20YiIDNucaNdhMwPDl3LGjBkyePBgGThwoDRs2FDmzZsnpUqVkoULFwa7aEREFGSGTuzIzs6WXbt2SWJionMfBgJ27txZtm3b5vUxmLDTddJOow2iJCK63nI0m9oCPYYZGLomlp6eLjk5OVK1alW3/bidlpbm9THJyclqsJ9j40BnIgrVRTFzAtzMwByl9ANqbRhh7tgwAp6IKJTYtTBdNjMwdHNiVFSUFCtWTM6ePeu2H7erVavm9TERERFqIyIi6zN0qA0PD5eWLVvK+vXr3Sb0xe22bdsGtWxEREaVE0LNiYauiQHS6wcMGCCtWrWSuLg4mTlzply6dEllKxIRUW52HRIzcAwzMHwQ69Wrl5w/f14mTJigkjliY2NlzZo1uZI9iIgo9Bg+iMHw4cPVRkREBbPrMFjZLIOdTRHEiIjIdzk6TBvFaaeIiIiuM9bE/BQfHavLcdae3qPLcYiIPHE9MSIiMq0cNicSEREZH2tiREQWk6PDYGUOdiYioqCwaza1BXoMMzBHqCUiIvKCNTEiIoux69CcyMHOREQUFHYdllLhUixERBQUOWJTW6DHMANzhFoiIiIvWBMjIrIYO5sTiYjIrHJ0aA7EMczAHKGWiIjIC9bEiIgsxs7mRCIiMqscTgBMRETknzlz5kjt2rUlMjJS2rRpIykpKfnef+bMmVK/fn0pWbKkxMTEyDPPPCNXrlzx6zkZxIiILEb7Yz2xQDYcwx9Lly6VhIQESUpKkt27d0uzZs0kPj5ezp075/X+ixcvlnHjxqn779+/X9599111jOeff96v52UQIyKyaHNiToCbP2bMmCGDBw+WgQMHSsOGDWXevHlSqlQpWbhwodf7b926Vdq3by+PPPKIqr117dpV+vTpU2DtzRP7xEy+QjRwlWgiul4yMzPdbkdERKjNVXZ2tuzatUsSExOd+8LCwqRz586ybds2r8dt166dfPjhhypoxcXFyS+//CKrV6+Wfv36+VU+BjEiIoux67gUC/qqXKH5b+LEiW770tPTJScnR6pWreq2H7cPHDjg9fiogeFxt99+u2iaJteuXZOhQ4f63ZzIIEZEZDE5Oi6KeeLECSlXrpxzv2ctrLA2btwoU6ZMkbfeekslgRw5ckRGjhwpkyZNkvHjx/t8HAYxIiLKEwKYaxDzJioqSooVKyZnz55124/b1apV8/oYBCo0HT7++OPqdpMmTeTSpUsyZMgQeeGFF1RzpC+Y2EFEZNHmRHuAm6/Cw8OlZcuWsn79+v8vg92ubrdt29brYy5fvpwrUCEQApoXfcWaGBGRxdglLOBFLf19PNLrBwwYIK1atVKJGhgDhpoVshWhf//+UqNGDUlOTla3u3fvrjIamzdv7mxORO0M+x3BzPRBDC922bJlqmMQg+GQzTJt2jQ1OI6IiLzL0WxqC4S/j+/Vq5ecP39eJkyYIGlpaRIbGytr1qxxJnukpqa61bxefPFFsdls6r+nTp2SG264QQWwyZMn+/W8Ns2felsRu+eee6R3797SunVrlbmCrJV9+/bJTz/9JKVLl/Y5PbR8+fLSUXpIcVsJsSKm2BOZW+YFu1S85RfJyMgosP/Jl9+7Jzf3lIgygf3eZV28KnM7LAu4TNeboWtiiOKuFi1aJFWqVFHjEe64446glYuIKFRS7I3O0EHME64IoFKlSnneJysrS215DdQjIrI6TYdZ7HEMMzBHKf/IdBk1apSapqRx48b59qOhOu3YPAfqERGRdZgmiA0bNkz1hy1ZsiTf+2HaE9TYHBsG6hERhZIcsemymYEpmhOHDx8uq1atkk2bNknNmjXzva+3eb2IiEKJXQu8TwvHMANDBzEkTj799NOyfPlyNUVJnTp1gl0kIiIykOJGb0LEmjOff/65lC1bVo09APR1YdwYERHlZtchsSPQxxcVQ5dy7ty5ql+rY8eOUr16deeGhdOIiMi7QBfEdGxmYOiamIHHYRMRkQEYOogREZE5pp0KFgYxIiKLsYdQnxiDmAXER8fqchzOwUhEZsMgRkRkMXYkZgQ6ToyJHUREFAyaDtmFOIYZMIgREVmMPYRmsTdHzx0REZEXrIkREVmMndmJRERkVnY2JxIRERkfa2JERBZj1yE7kSn2REQUFHY2JxIRERkfa2JERBZjD6GaGIMYEZHF2EMoiLE5kYiITIs1MSIii7GHUE2MQYyIyGI0HVLkcQwzYBAjIrIYewjVxNgnRkREpsWaGOm+QjRwlWii4LGHUE2MQYyIyGLsIRTE2JxIRESmxZoYEZHF2EOoJsYgRkRkMZpmU1ugxzADNicSEZFpmSqITZ06VWw2m4waNSrYRSEiMvx6YvYANzMwTXPijh075O2335amTZsGuyhERIZmD6E+MVPUxC5evCh9+/aVBQsWSMWKFYNdHCIiMghTBLFhw4ZJt27dpHPnzgXeNysrSzIzM902IqJQTOzQAtzMwPDNiUuWLJHdu3er5kRfJCcny0svvXTdy0VEZFR2Nicaw4kTJ2TkyJHy0UcfSWRkpE+PSUxMlIyMDOeGYxARkTUZuia2a9cuOXfunLRo0cK5LycnRzZt2iSzZ89WTYfFihVze0xERITaiIhClRZC48QMHcQ6deoke/fudds3cOBAadCggYwdOzZXACMiIlEBKNDmQAYxHZQtW1YaN27stq906dJSuXLlXPuJiOj/F7TUAlzV0iyLYhq6T4yIiMi0NTFvNm7cGOwiEBEZml1s6n+BHsMMTBfEiIgof1oIJXawOZGIiEyLNTG6LuKjY3U71trTe3Q7FlEosGs2sYXIYGcGMSIii9E0HbITTZKeyOZEIiIyLdbEiIgsRguhxA4GMSIii9FCKIixOZGIiEyLNTEiIouxMzuRiIjMSmN2IhERkfGxJkZEZMmamC3gY5gBgxgRkcVoIZSdyCBGRGTF9cQk8GOYAfvEiIjItFgTIyKyGI3NiUREZFpa6LQnsjmRiIh0MWfOHKldu7ZERkZKmzZtJCUlJd/7//777zJs2DCpXr26REREyC233CKrV6/26zlZEyMishot8OZEHMMfS5culYSEBJk3b54KYDNnzpT4+Hg5ePCgVKlSJdf9s7OzpUuXLupvn376qdSoUUOOHz8uFSpU8Ot5GcSIiCxGC8KMHTNmzJDBgwfLwIED1W0Esy+//FIWLlwo48aNy3V/7P/1119l69atUqJECbUPtTh/sTmRiIjylJmZ6bZlZWV5rVXt2rVLOnfu7NwXFhambm/bts3rcVeuXClt27ZVzYlVq1aVxo0by5QpUyQnJ0f8wZoYGV58dKwux1l7eo8uxyEKpezEmJgYt/1JSUkyceJEt33p6ekq+CAYucLtAwcOeD3+L7/8It9++6307dtX9YMdOXJEnnrqKbl69ap6Dl8xiBERWY1m87tPy+sxROTEiRNSrlw5524kYOjBbrer/rD58+dLsWLFpGXLlnLq1Cl57bXXGMSIiEgfCGCuQcybqKgoFYjOnj3rth+3q1Wr5vUxyEhEXxge53DrrbdKWlqaap4MDw/3qXzsEyMismhihxbg5isEHNSk1q9f71bTwm30e3nTvn171YSI+zkcOnRIBTdfAxgwiBERWXWwsxbg5gek1y9YsEDef/992b9/vzz55JNy6dIlZ7Zi//79JTEx0Xl//B3ZiSNHjlTBC5mMSOxAooc/DB/E0Eb66KOPSuXKlaVkyZLSpEkT2blzZ7CLRURELnr16iXTp0+XCRMmSGxsrOzZs0fWrFnjTPZITU2VM2fOOO+PhJG1a9fKjh07pGnTpjJixAgV0Lyl45u2T+y3335TVc677rpLvvrqK7nhhhvk8OHDUrFixWAXjYjIsLQgzZ04fPhwtXmzcePGXPvQ1Lh9+3YJhKGD2LRp01S0fu+995z76tSpE9QyERGZgiYhwdDNiRgM16pVK/nLX/6iUjGbN2+u2lyJiChvjppYoJsZGDqIYTDc3LlzpV69eqrtFB2BaDdFx2FeMJrcc4Q5ERFZk6GbE5F6iZoYMlYANbF9+/apObkGDBjg9THJycny0ksvFXFJiYgMRONSLIaA8QINGzZ024fBcMhyyQtSODMyMpwbRpsTEYUWm06b8Rm6JobMREzj7wrjCW688cY8H4MpUfSaFoWIiIzN0DWxZ555RqVfojkRI7sXL16s5tnydzAcEVFI0Yp+sHOwGDqItW7dWpYvXy4ff/yxmqZ/0qRJaqE1zHpMRER5CKEgZujmRPjzn/+sNiIiIl1qYkiWOHnypPN2SkqKjBo1SjX1ERGRQZZi0QLcrBrEHnnkEdmwYYP6N6bN79KliwpkL7zwgrz88st6l5GIiAw8i73pmhMxVisuLk79+5NPPlH9Vd9//718/fXXMnToUDUBJJFVV4gGrhJNZOIghuWjHWns33zzjdx3333q3w0aNHCbpZiIiIJA42DnfDVq1EjNmrF582ZZt26d3HPPPWr/6dOn1ZIpREQURBr7xAqcXf7tt9+Wjh07Sp8+faRZs2bOCXsdzYxERESGbE5E8EpPT1eT67qu7TVkyBApVaqUnuUjIiI/2bT/bYEew7I1saSkJJVi77k4Ze3atdWSKUREFERa6Ax2LlQQ+/zzz6Vu3brSqVMnNRUUlj8hIiKD0Ngnlq89e/bIjh07VILHyJEjpVq1amqtL+wjIiIy/NyJWNvrjTfeUBmJ7777rmpexKzzTZs2lVmzZqllUIiIKAg0Nif6TNM0NW4sOztb/Rv9ZLNnz5aYmBhZunSpPqUkIiLfaQxiBdq1a5cMHz5cLVyJJVNQM9u/f7989913cvjwYZk8ebKMGDFC39ISEREFGsSaNGkit912mxw9elQ1JWJC4KlTp8rNN9/svA/Gj50/f74whyciokBooVMTK9Q4sYcfflgGDRokNWrUyPM+UVFRYrfbAykbEREVhqZDdqFVsxPR/7Vo0SI10JmIiMhUNbESJUrIlStXrk9piIgoYDbO2JG/YcOGqfkTr127pn+JiIgoMBr7xPKFQc3r169X64chyaN06dJuf1+2bJle5SMiItI3iFWoUEEefPDBwjyUiIgouEHsvffe068ERESkK5sOfVo2KwcxQH/Yxo0b5eeff5ZHHnlEypYtq6agKleunJQpU0bfUhIZTHx0rG7HWnt6j27HIgo1hQpix48fV6s5p6amqhnsu3TpooIYkj1wG6s+ExFRkGgcJ5YvzFzfqlUr+e2336RkyZLO/Q888IBK+CAioiDSmJ2Yr82bN8vWrVslPDw816KYp06d0qtsRERUGHoEIZMEsULVxDCdVE5OTq79WI4FzYpERESGDWJdu3aVmTNnOm/bbDa5ePGiJCUlyZ/+9Cc9y0dERIWcscMW4GbZIPb666/L999/Lw0bNlRTUCE70dGUiOQOvaC2N378eKlTp47qe6tbt65MmjRJrVtGRER5YJ9Y/mrWrCk//vijLFmyRP71r3+pWthjjz0mffv2dUv0CBQC4ty5c+X999+XRo0ayc6dO2XgwIFSvnx5rlVGRESFHydWvHhxefTRR+V6QvJIjx49pFu3buo2ansff/yxpKSkXNfnJSIyNS10EjsKFcQ++OCDfP/ev39/0UO7du1k/vz5cujQIbnllltU7W/Lli0yY8aMPB+DcWrYHLhkDBGFGlsIzWJfvLDjxDzXGLt8+bJKuS9VqpRuQWzcuHEqCDVo0ECKFSum+sgmT56smi3zkpycLC+99JIuz09ERBZM7MAgZ9cNfWIHDx6U22+/XTX36eWTTz6Rjz76SBYvXiy7d+9WfWPTp09X/81LYmKiZGRkOLcTJ07oVh4iIlPN2KEFuFm5T8xTvXr1ZOrUqaqf7MCBA7occ8yYMao21rt3b3Uby75gyivUtgYMGOD1MREREWojIgpZWuj0iRWqJpZfsgcmAdYLmijDwtyLiGZFDLYmIiIqVE1s5cqVbrcxbuvMmTMye/Zsad++vV5lk+7du6s+sFq1aqkU+x9++EEldQwaNEi35yAishobEzvyd//997vdxowdN9xwg9x9991qILRe3nzzTTXY+amnnpJz585JdHS0PPHEEzJhwgTdnoOIyHK00GlOLFQQK6rmPMzDiOmtXKe4IiKiAmg61KSsHMQSEhJ8vm9+Y7qIiIiKPIihbwop71jduX79+mofBiQj6aJFixZuzYxERFTENDYnFphwgaY+jNeqWLGi2ofxYpjXsEOHDjJ69Gi9y0lkWfHRsbocZ+3pPbochyxAC50gVuhZ7DFWyxHAAP9+5ZVXdE3sICIi0r0mhqmgzp8/n2s/9l24cKEwhyQiIp3YQijFvlA1sQceeEA1HS5btkyt5ozts88+U8ux9OzZU/9SEhER6VUTmzdvnjz77LNqMUxM/qsOVLy4CmKvvfZaYQ5JRERUNEEMM9W/9dZbKmD9/PPPah9WXS5dunRhDkdERHrSQiexI6AJgBG0mjZtql9piIgoYDb2iREREYXQUixERGQgmoQEBjEiIqvRQqdPjM2JRERkWqyJERFZjC2EEjsYxIiIrEYLneZEBjEiIouxhVBNjH1iRERkWgxiRERWbU7UAtz8NGfOHKldu7ZERkZKmzZtJCUlxafHLVmyRK0/ef/99/v9nAxiRERWoxV9EFu6dKkkJCRIUlKSWjS5WbNmEh8fL+fOncv3cceOHVNz8WItysJgECMiooDNmDFDBg8erFY4adiwoZooHvPsLly4MM/H5OTkSN++feWll16Sm266qVDPy8QOIovQa4Vo4CrR5mbTMbED60e6ioiIUJur7Oxs2bVrlyQmJjr3hYWFSefOnWXbtm15PsfLL78sVapUUSugbN68uVDlZE2MiMhqNP2aE2NiYqR8+fLOLTk5OdfTpaenq1pV1apV3fbjdlpamtcibtmyRd59911ZsGBBQC+VNTEiIsrTiRMnpFy5cs7bnrWwwrhw4YL069dPBbCoqKiAjsUgRkRkNZp+g50RwFyDmDcIRMWKFZOzZ8+67cftatWq5bo/1qFEQkf37t2d++x2u3OB5YMHD6o1Kn3B5kQiIov2idkC3HwVHh4uLVu2lPXr17sFJdxu27Ztrvs3aNBA9u7dK3v27HFu9913n9x1113q32jC9BVrYkREFDCk1w8YMEBatWolcXFxMnPmTLl06ZLKVoT+/ftLjRo1VJ8axpE1btzY7fEVKlRQ//Xcb+ia2KZNm1R1Mjo6Wg10W7FihdvfNU2TCRMmSPXq1aVkyZIq0+Xw4cNBKy8RkSloRT9OrFevXjJ9+nT1mx0bG6tqVGvWrHEme6SmpsqZM2d0f6lBrYkhSmNA3KBBg6Rnz565/v7qq6/KG2+8Ie+//77UqVNHxo8frwbP/fTTTyqSExGRceZOHD58uNq82bhxY76PXbRokfmC2L333qs2b1ALQ3X0xRdflB49eqh9H3zwgYrqqLH17t27iEtLRERGY9jEjqNHj6rxBWhCdMAYBczHld/guaysLDU4z3UjIgopWnDmTgwGwwYxxwA5fwbPAToNXQfm+ZPlQkRkCRqDmGlh2pOMjAznhoF6REShxKbTZgaGDWKOAXK+Dp5zHU3uGJznyyA9IiIyL8MGMWQjIli5Dp5D/9Y///lPr4PniIgo9JoTg5qdePHiRTly5IhbMgfGFlSqVElq1aolo0aNkldeeUXq1avnTLHHmLLCLJxGRBQqbEFKsQ+5ILZz5041zYjriG/AqG+MGXjuuefUWLIhQ4bI77//LrfffrsaPMcxYkREFPQg1rFjRzUeLC+YxQPrzWAjIqKinwDY6Dh3IhGRFWkSEgyb2EFERFQQ1sSIKJf46FhdjrP29B5djkP+sTGxg4iITEsLnT4xNicSEZFpsSZGRGQxNjYnEhGRaWlsTiQiIjI81sSIiCzGxuZEIiIyLS10mhMZxIiIrEYLnSDGPjEiIjIt1sSIiCzGxj4xIiIyLY3NiURERIbHmhgRkcXYNE1tgR7DDBjEiIisRmNzIhERkeGxJkZEZDE2ZicSEZFpaaHTnMggRkSGXyEauEo0ecMgRkRkMTY2JxIRkWlpodOcyOxEIiIyLdbEiIgsxsbmRCIiMi2NzYlFYtOmTdK9e3eJjo4Wm80mK1ascP7t6tWrMnbsWGnSpImULl1a3ad///5y+vTpYBaZiMhUtTFbITezCGoQu3TpkjRr1kzmzJmT62+XL1+W3bt3y/jx49V/ly1bJgcPHpT77rsvKGUlIiLjCWpz4r333qs2b8qXLy/r1q1z2zd79myJi4uT1NRUqVWrVhGVkojIZDTtf1ugxzABU/WJZWRkqGbHChUq5HmfrKwstTlkZmYWUemIiIzBFkKJHaZJsb9y5YrqI+vTp4+UK1cuz/slJyerWpxji4mJKdJyEhFR0TFFEEOSx8MPPyyapsncuXPzvW9iYqKqsTm2EydOFFk5iYgMlZ2oBbiZQHGzBLDjx4/Lt99+m28tDCIiItRGRBSqbPb/bYEewwyKmyGAHT58WDZs2CCVK1cOdpGIiMhAghrELl68KEeOHHHePnr0qOzZs0cqVaok1atXl4ceekil169atUpycnIkLS1N3Q9/Dw8PD2LJiYgMTAudwc5BDWI7d+6Uu+66y3k7ISFB/XfAgAEyceJEWblypbodG+u+nANqZR07dizi0hIRmYMthLITgxrEEIiQrJGX/P5GRERk6D4xIiIqBI2DnYmIyKRsbE4kIjKW+Gj3vvFArD29R7djUXAxiBERWY3G7EQiIjIpG5sTiYjItLTQSewwxdyJRERE3rAmRkRkMTY2JxIRkWlpoZPYweZEIiIyLdbEiIgsxsbmRCIiMi279r8t0GOYAJsTiYjItFgTIyKyGi10EjsYxIiILMamQ58WjmEGbE4kIiLTYk2MiMhqNE47RUREJk+xtwW4+WvOnDlSu3ZtiYyMlDZt2khKSkqe912wYIF06NBBKlasqLbOnTvne/+8MIgREVk1sUMLcPPD0qVLJSEhQZKSkmT37t3SrFkziY+Pl3Pnznm9/8aNG6VPnz6yYcMG2bZtm8TExEjXrl3l1KlTfj0vgxgREQVsxowZMnjwYBk4cKA0bNhQ5s2bJ6VKlZKFCxd6vf9HH30kTz31lMTGxkqDBg3knXfeEbvdLuvXr/freRnEiIgsxqZpumyQmZnptmVlZeV6vuzsbNm1a5dqEnQICwtTt1HL8sXly5fl6tWrUqlSJb9eKxM7iCjkxEfH6nKctaf3iCHZ/9gCPYaIauZzhebCiRMnuu1LT0+XnJwcqVq1qtt+3D5w4IBPTzd27FiJjo52C4S+YBAjIqI8nThxQsqVK+e8HRERIXqbOnWqLFmyRPWTISnEHwxiREQWY3NpDgzkGIAA5hrEvImKipJixYrJ2bNn3fbjdrVq1fJ97PTp01UQ++abb6Rp06Z+l5N9YkREVqMVbXZieHi4tGzZ0i0pw5Gk0bZt2zwf9+qrr8qkSZNkzZo10qpVq0K9VNbEiIgoYEivHzBggApGcXFxMnPmTLl06ZLKVoT+/ftLjRo1JDk5Wd2eNm2aTJgwQRYvXqzGlqWlpan9ZcqUUZspamKbNm2S7t27q848m80mK1asyPO+Q4cOVffBG0NERPlwzNgR6OaHXr16qaZBBCakze/Zs0fVsBzJHqmpqXLmzBnn/efOnauyGh966CGpXr26c8MxTFMTQ5TGgLhBgwZJz54987zf8uXLZfv27SrYERGRMRfFHD58uNq8QdKGq2PHjokeghrE7r33XrXlB6O3n376aVm7dq1069atyMpGRETGZ+g+MXQM9uvXT8aMGSONGjXy6TEYiOc6GA+D84iIQorGCYANAR1/xYsXlxEjRvj8GHQali9f3rl5DtQjIrI6m12fzQwMG8QwhcmsWbNk0aJFKqHDV4mJiZKRkeHcMFCPiIisybBBbPPmzWr241q1aqnaGLbjx4/L6NGjVTpmXjCa3DE4z5dBekRElqMVfXZisBi2Twx9YZ5zaGFaf+x3jDsgIiIvCrGUitdjmEBQg9jFixflyJEjzttHjx5VYwswizFqYJUrV3a7f4kSJdQUJvXr1w9CaYmIQm/aKaMLahDbuXOn3HXXXW4jvgGjvtEXRkREZNgg1rFjR9H8iPZ6DY4jIrI0LXRS7A3bJ0ZERIWk6bCemDlimHGzE4mIiArCmhgRUZBXiL6mXRWRX0QvNiZ2EBGRuVPstcCPYQJsTiQiItNiTYyIyGo0ZicSEZFZ2dGppcMxTIDNiUREZFqsiRERWYyN2YlERGRaWuj0ibE5kYiITIs1MSIiq9FCpybGIEZEZDUagxgREZmVnSn2REREhseaGBGRxdiYYk9ERKalhU6fGJsTiYjItFgTIyKyGruG9sDAj2ECDGJERFajhU5zouWDmPbHB3FNrppmkTciCi3q98nl94p8Z/kgduHCBfXfLbI62EUhIirw96p8+fI6HEnToSZljoBq+SAWHR0tJ06ckLJly4rN5n30X2ZmpsTExKj7lStXTozAiGUyarlYJt+wTMYtF2pgCGD4vdLpgMLmRIsICwuTmjVr+nRfnKxG+iIZtUxGLRfL5BuWyZjl0qcGFnosH8SIiEKOHbUoZicSEZEZafb/bYEewwQ42FlEIiIiJCkpSf3XKIxYJqOWi2XyDctk/nJRbjaNOZ1ERJaQmZmp+tY6xzwpxcMCC8DX7FnyzYm5kpGRYcj+Sgc2JxIRWY2dfWJERGRWWuik2LNPjIiITIs1MSIiq9F0qEmZoyLGmhiFro4dO8qoUaOCXQyi69ecqAW4mQCDGBERmRabE4mIrMaOgcp2HY5hfKyJEf3hyy+/VGNsPvroIzXx68MPPywVKlSQSpUqSY8ePeTYsWPqfps2bZISJUpIWlqa2+PRNNmhQwf17+PHj0v37t2lYsWKUrp0aWnUqJGsXs2VFKiIaGxOJAopixcvlj59+qgAhuAVHx+vVj7YvHmzfP/991KmTBm55557JDs7W+644w656aab5O9//7vz8VevXlWPHTRokLo9bNgwycrKUgFv7969Mm3aNHUMItIXmxMp5M2ZM0deeOEF+eKLL+TOO++UDz/8UOx2u7zzzjvO5Xvee+89VSvbuHGjdO3aVR577DG1b8yYMerveOyVK1dUAITU1FR58MEHpUmTJuo2gh5RkdFCZ5wYgxiFtE8//VTOnTunalutW7dW+3788Uc5cuSIqom5QpD6+eef1b//+te/yosvvijbt2+X2267TRYtWqQCGJoOYcSIEfLkk0/K119/LZ07d1YBrWnTpkF4hRSS7KEzYwebEymkNW/eXG644QZZuHChc2n4ixcvSsuWLWXPnj1u26FDh+SRRx5R96lSpYrq80Jt7OzZs/LVV185mxLh8ccfl19++UX69eunmhNbtWolb775ZtBeJ5FVsSZGIa1u3bry+uuvqzFjxYoVk9mzZ0uLFi1k6dKlKlDlN/EpAhX60bDoKo7Tvn17t79jZeChQ4eqLTExURYsWCBPP/10EbwqCnWaZldboMcwA9bEKOTdcsstsmHDBvnss89UhmHfvn0lKipKZSQisePo0aOqLwxNhCdPnnQ+DskfCHKvvPKKDBw40O2YOM7atWvVY3fv3q2Of+uttwbh1VFI0rT/NQcGsrFPjMg86tevL99++62zRoaswrFjx0rPnj3lwoULUqNGDenUqZNbzSwsLEz1jU2ZMkX69+/vdrycnByVoYigh8cgs/Fvf/tbEF4ZkbVxPTGiACBL8fz587Jy5cpgF4VIHOuJdSrfT4rbwgM61jUtW9Zn/J3riRFZEb7YSNjA+DIGMDIcu13EFmCflkn6xBjEiAoB/WUpKSkqaaNLly7BLg6RO9XAxnFiRJQHJHoQUfAxiBERWYxmt4tmC40UewYxIiKr0UKnOZHjxIiIyLRYEyMishq7hgFUIVETYxAjIrIaDQHIHhJBjM2JRERkWqyJERFZjGbXRAuwOdEskzmxJkZEZDWaXZ+tEAvM1q5dWyIjI6VNmzZqQoD8/OMf/5AGDRqo+2MB2dWrV/v9nAxiREQUMCxflJCQIElJSWrlhmbNmqmVHrDorDdbt25VSxlh/tEffvhB7r//frXt27fPr+flBMBERBabALij7QEpbisR0LGuaVdlo7bc5wmAUfPC6uhYkw/sdrtaUw9r6I0bNy7X/Xv16iWXLl2SVatWOfdhlfTY2FiZN2+ez+VkTYyIyGq0om1OzM7Oll27dknnzp3dlirC7W3btnl9DPa73h9Qc8vr/nlhYgcRkcVck6sBT9ihjvFH7c5VRESE2lylp6erNfSqVq3qth+3Dxw44PX4aWlpXu+P/f5gECMisojw8HCpVq2abEnzP0HCmzJlyqgmQVfo85o4caIYBYMYEZFFREZGytGjR1Xznh6QMmGz2dz2edbCICoqSq2IfvbsWbf9uI2g6g32+3P/vDCIERFZLJBFRkYWeQ2wZcuWsn79epVh6EjswO3hw4d7fUzbtm3V30eNGuXct27dOrXfHwxiREQUMKTXDxgwQFq1aiVxcXEyc+ZMlX04cOBA9ff+/ftLjRo1JDk5Wd0eOXKk3HnnnfL6669Lt27dZMmSJbJz506ZP3++X8/LIEZERAFDyvz58+dlwoQJKjkDqfJr1qxxJm+kpqaqjEWHdu3ayeLFi+XFF1+U559/XurVqycrVqyQxo0b+/W8HCdGRESmxXFiRERkWgxiRERkWgxiRERkWgxiRERkWgxiRERkWgxiRERkWgxiRERkWgxiRERkWgxiRERkWgxiRERkWgxiRERkWgxiREQkZvV/0HM9KFebrHoAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 480x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def generate_look_ahead_mask(size):\n",
    "    \"\"\"\n",
    "    生成上三角掩码(look-ahead mask)，用于Transformer解码器中的自注意力机制\n",
    "    \n",
    "    Args:\n",
    "        size (int): 掩码的大小 (size x size)\n",
    "        \n",
    "    Returns:\n",
    "        torch.Tensor: 布尔类型的上三角掩码，其中True表示被掩盖的位置\n",
    "    \"\"\"\n",
    "    # 创建上三角矩阵(1s在上三角，包括对角线；0s在下三角)\n",
    "    mask = torch.triu(torch.ones(size, size), diagonal=1).bool()\n",
    "    \n",
    "    # 在解码器的自注意力中，我们通常掩盖未来的位置(右上方)\n",
    "    # 所以需要转置使得右上方为True(被掩盖)\n",
    "    return mask\n",
    "    \n",
    "# 测试不同大小的look-ahead mask\n",
    "print(\"Look-ahead mask for size 3:\")\n",
    "print(generate_look_ahead_mask(3))\n",
    "print(\"\\nLook-ahead mask for size 5:\")\n",
    "print(generate_look_ahead_mask(5))\n",
    "plt.matshow(generate_look_ahead_mask(16))\n",
    "plt.colorbar()\n",
    "plt.xlabel(\"keys\")\n",
    "plt.ylabel(\"querys\")\n",
    "plt.title(\"1 means mask while 0 means unmask\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "a473db90",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['[BOS]', '[UNK]', 'quick', 'brown', '[UNK]', 'jumps', 'over', 'the', '[UNK]', 'dog', '.', '[EOS]']\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1IAAAG1CAYAAADz+MUUAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAQQBJREFUeJzt3Qd8FNXa+PEnIRBpCYgIBKJ0pAoGBA0lXJrYQH0pggKCogheVEDJRUVRiYCgr1gQERRBynsFQa9Ik9ARUEJRUboJEQWBJKIEQub/eY7/3ZtGkknZ+vt+PpOdnZ2dMzObnWefM2fOBFiWZQkAAAAAIN8C8z8rAAAAAECRSAEAAACATSRSAAAAAGATiRQAAAAA2EQiBQAAAAA2kUgBAAAAgE0kUgAAAABgE4kUAAAAANhEIgUAAAAANpFIAZfxwQcfSEBAgBw9ejTT9ClTpkjt2rWlRIkS0rx5c7etn697/vnnzf4/deqUu1cFAPIdI4rKoEGDpFy5cuLq7dm5c6d4WhzwVsQx30ciBdiwatUqeeqppyQyMlLmzJkjEydOLNLlb9myxRx4z549m+01LevTTz8t0vIAAABQMCRSgA1fffWVBAYGyvvvvy8DBgyQW2+9tcgTqRdeeIFECgAAwMORSAE2/Pbbb1K6dGkpVaqUu1cFAAAAbkQiBZ+TkpIijz/+uNSsWVOCg4Pl6quvli5dusi3337rnOfrr7+WW265RUJDQ6VMmTLSoUMH2bx5c67L1XbO2pzv3LlzZlwHbVOeH3v27DHt3fXaqiuuuEKqVq0qgwcPlt9//905jzbpGzNmjBmvVauWswxtf6+PWu6HH37onK7Lc7xPnx88eNBMq1ChgtmuBx54QP78888Ctcn/+eef5fbbbzfj1atXl7feesu8vnfvXvnHP/4hZcuWlWuvvVY+/vjjTO8/ffq0jB49Wpo2bWreGxISIt27d5fdu3dnK2v69OnSuHFjs/8rVqwoLVu2zLa8rI4dOyZ169aVJk2ayK+//mpr2wCguL399tvmuKaxJywsTIYPH55jC4P/+7//k4iICFMxd9VVV8l9990nx48fz3P5cXFxUrlyZYmKipI//vgjX+ukx81HH31UGjRoYMqrVKmS9OrV67LXdqWmpsqTTz5pytFj/V133SUnT57MNt+KFSukXbt2Zp7y5cvLbbfdJt99953t2OewadMmadWqlZmvTp068u6770pBEMfgSkEuLQ1wgUceeUT+/e9/y4gRI6RRo0bmgK0H6B9++EFuuOEG0zxPD4oaxMaPH2+a6mmCpAfWjRs3yo033pjjcj/66COZOXOmbN++XWbNmmWm3Xzzzflap9WrV8vhw4dNcqOBRIONLksft23bZhKhu+++W3766SdZsGCBvPbaaya4Kg1mWvaDDz5o1m3o0KFmugaajHr37m0SsJiYGJM06jpqEjlp0iRb++/SpUtm/7Rv314mT54s8+fPN/tSg864ceOkf//+Zl1nzJhhmjfedNNNplyl26jNDzVI6zQNEhoMNVH9/vvvzQ8L9d5778k///lP+Z//+R8ZOXKknD9/3gRcTXD79euX43odOnTIfEZXXnml2Z+O/QMAnkArtbRpdufOnWXYsGHy448/yjvvvCM7duwwFXUlS5Y082kFnMYCTRr0eK3Hyf/93/818+zatctUhuVEl9OtWzfzY33ZsmUmKcoPfZ82G+/bt6/UqFHDJFC6XpqM6XFZk4CMHnvsMZMUaHzUeV9//XUTAxYtWuScR2PSwIEDzfpojNFKO11m27ZtzTZoRWZ+Y58juenatauJd7of09LSTPlVqlQp0GdBHIPLWICPCQ0NtYYPH57ja+np6Va9evWsbt26mXGHP//806pVq5bVpUsX57Q5c+ZY+hU5cuSIc9rAgQOtsmXL2l4nXX5WCxYsMMvfsGGDc9qUKVOylemg5Wr5WY0fP968Z/DgwZmm33XXXValSpVsracuX5c1ceJE57QzZ85YpUuXtgICAqyFCxc6p+/fv9/Mq+U7nD9/3rp06VKmZeq2BAcHWxMmTHBO69Gjh9W4ceNc18WxXSdPnrR++OEHKywszGrVqpV1+vRpW9sEAMUhY4z47bffrFKlSlldu3bNdAx88803zTyzZ882zy9cuGBdffXVVpMmTay//vrLOd/nn39u5nvuuedyjDebNm2yQkJCrNtuu80cZwsbf7Zu3WrKmzt3brbt6dy5c6b4+MQTT1glSpSwzp49a56npKRYFSpUsB566KFMyzxx4oSJvxmn5zf29ezZ07riiiusY8eOOad9//33ply7P1WJY3AlmvbB52htntYIJSYm5tgs4sCBA6a2SM9UaZekOmizuU6dOsmGDRskPT29yNcpY82h1lppmW3atDHPMzY5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",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n",
      "['[BOS]', '[UNK]', 'does', 'the', '[UNK]', 'say', '?', '[EOS]', '[PAD]', '[PAD]', '[PAD]', '[PAD]']\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1AAAAG1CAYAAAD3DRUpAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAQFFJREFUeJzt3QucTfX6+PFnxjCuM3TDNFMhl5SQCX/CIINuk1tCIU4KlQrJq+MoyiQVujJ1kBK5TFdpiNxCumA4RW4xYyiOmplcxmXW//V8z2vv38wYZs2Y2XvtvT/v12uZtddee33X3mavZ57vetZ3BVmWZQkAAAAAoEDBBa8CAAAAAFAkUAAAAABgEwkUAAAAANhEAgUAAAAANpFAAQAAAIBNJFAAAAAAYBMJFAAAAADYRAIFAAAAADaRQAEAAACATSRQQD5mzZolQUFB8ttvv+VaPmnSJKlZs6aUKlVKGjVq5LX983fPPvus+fyPHDni7V0BANsxorj0799fKlasKJ5+Pz/88IM4LQ74KuKYfyOBAmxaunSpPPXUU9KyZUuZOXOmTJgwoVi3v27dOnPA/euvv855Ttv65JNPirU9AAAAFB4JFGDTihUrJDg4WP79739L37595bbbbiv2BOq5554jgQIAAHAwEijApj/++EPKlSsnZcqU8fauAAAAwEtIoOBXMjMz5fHHH5drrrlGQkND5YorrpAOHTrITz/95F7nu+++k06dOkl4eLiUL19e2rRpI99+++0Ft6t1zFq2d+zYMTOvk9aM25GcnGzq2fXaqbJly0q1atVkwIAB8t///te9jpbujRw50szXqFHD3YbW1+tPbfe9995zL9ftuV6nj3ft2mWWVa5c2byvBx54QI4fP16kmvv9+/fLHXfcYeavvPJKefPNN83zW7dulXbt2kmFChXk6quvlg8//DDX648ePSojRoyQBg0amNeGhYVJ586dZcuWLee09frrr8v1119vPv8qVapIdHT0OdvLa9++fXLttdfKDTfcIL///nuh3hsAlLS33nrLHNc09kRERMjQoUPzrShYsGCBNGnSxHTIXXbZZXLffffJgQMHCtz+5s2b5fLLL5eYmBj5+++/be2THjeHDBkidevWNe1deuml0qNHj/Neu5WVlSVPPvmkaUeP9V26dJHDhw+fs96SJUukVatWZp1KlSrJ7bffLv/5z38KHftc1q5dKzfffLNZr1atWjJ9+nQpCuIYPCXEYy0BHvDwww/LwoUL5ZFHHpH69eubA7UemH/55Re56aabTBmeHgw1eI0dO9aU5GlipAfUNWvWSNOmTfPd7vvvvy8JCQmyceNGeffdd82yFi1a2NqnZcuWyZ49e0xSowFEg4xuS39u2LDBJEBdu3aVX3/9VebOnSuTJ082QVVpENO2//GPf5h9GzRokFmuASane+65xyRe8fHxJlnUfdTkceLEiYX6/M6ePWs+n9atW8tLL70kc+bMMZ+lBptnnnlG+vTpY/Z12rRppozx//2//2faVfoetcxQg7Mu0+CgQVAT1J9//tn8QaHeeecdeeyxx6R79+4ybNgwOXnypAm0mtj27t073/3avXu3+T+65JJLzOfp+nwAwAm0M0tLsG+99VYZPHiw7NixQ95++235/vvvTQdd6dKlzXra8aaxQJMFPV7rcXLq1KlmnU2bNplOsPzodjp27Gj+SP/0009NMmSHvk7Lw++9916JjIw0iZPulyZhelzWP/5zevTRR00yoPFR150yZYqJAR999JF7HY1J/fr1M/ujMUY763Sbt9xyi3kP2oFpN/a5kprY2FgT7/RzPHPmjGm/atWqRfq/II7BIyzAj4SHh1tDhw7N97ns7Gyrdu3aVseOHc28y/Hjx60aNWpYHTp0cC+bOXOmpV+PvXv3upf169fPqlChQqH3Sbef19y5c832V69e7V42adKkc9p00Xa1/bzGjh1rXjNgwIBcy7t06WJdeumlhdpP3b5ua8KECe5lf/75p1WuXDkrKCjImjdvnnv59u3bzbravsvJkyets2fP5tqmvpfQ0FBr3Lhx7mVxcXHW9ddff8F9cb2vw4cPW7/88osVERFh3XzzzdbRo0cL9Z4AoCTkjBF//PGHVaZMGSs2NjbXMfCNN94w68yYMcM8PnXqlHXFFVdYN9xwg3XixAn3el988YVZ71//+le+8Wbt2rVWWFiYdfvtt5vj7MXGn/Xr15v2Zs+efc77ufXWW3PFxyeeeMIqVaqU9ddff5nHmZmZVuXKla0HH3ww1zYPHTpk4m/O5XZj3913322VLVvW2rdvn3vZzz//bNot7J+pxDF4CiV88Cvae6c9QGlpafmWP+zcudP0DumZKR1aVCctj2vfvr2sXr1asrOzi32fcvYUai+Vttm8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gE0kUAAAAABgEwkUAAAAANhEAgUAAAAANpFAAQAAAIBNJFAAAAAAYBMJFAAAAADYRAIFAAAAAL6QQK1cuVKioqIkISFBYmJipFWrVtK6dWvp3bu3nD171qyzdetWad++vbRp00buuOMOSUlJMcuTk5PNurq8RYsWcuDAAfn555+lUaNGMmLECG++LQCAjyIuAQAcfwaqZ8+eMmjQIDO/ZMkSWb16tVSsWFHWr18vp0+flvvuu88EslWrVsno0aPNYzV+/Hh5++23zfLly5fLpZdeKvXr15cpU6act62srCzJyMjINQEA4K24pIhNAOBbvJ5A5SczM1PCwsJkw4YNpueuVq1aZnnLli0lOzvb9PaVK1dOvv76azl27JiZL1u2bIHbjY+Pl/DwcPekvYwAAHgrLiliEwD4FkclUJ07d5bGjRtLamqqXHfddZKWliYRERG51omMjDTLJ02aJL/88os0bNjQ9BZqwCqI9hSmp6e7J1fZBQAA3ohLitgEAL7FUQmUlkps2rRJevToIa+88opUr17dBKWcNIhp8KpatapMmzZNdu3aJbVr15b333+/wO2HhoaaHsScEwAA3opLitgEAL7FUQmUS5UqVeSPP/6Q5s2by08//SS7d+82y7/99lvzU8sbdu7c6V7/8ssvF8uyvLa/AAD/RlwCALiEiMNKJUqVKmXqyd977z0pU6aMfPDBB/Lggw+a0Y8qVKhgHqt58+bJF198YerMK1eu7F4OAEBxIS4BAByVQOkFtsuWLTOjGenQsfnRWvIVK1acs3zMmDFmykmHi3366aflrrvuKrF9BgD4L+ISAKAgQVYA1xjoULE64lGMxElIUGlv7w6AC0hK2+ztXUAxysjMlip19phBE7jmxxmxie8YgECXYTM2OfIaKAAAAABwIhIoAAAAALCJBAoAAAAAbCKBAgAAAACbSKAAAAAAwCYSKAAAAACwiQQKAAAAAHzhRroAAMAZOkY08kq73H8KgK/hDBQAAAAA2EQCBQAAAAA2kUABAAAAgE0kUAAAAABgEwkUAAAAANhEAgUAAAAANpFAAQAAAIBNJFAAAAAAYBMJFAAAAADYRAIFAAAAADaRQAEAAACATSRQAAAAAOD0BGrlypUSFRUlCQkJEh0dnes51+P+/ftL586dz1murx0xYoSZX79+vbRs2VL++usv6du3r0RGRnr0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",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "inputs_words = [\"The quick brown fox jumps over the lazy dog .\", \"What does the fox say ?\"]\n",
    "\n",
    "#encode变为id\n",
    "inputs_ids, input_padding_mask = tokenizer.encode([w.split() for w in inputs_words], return_mask=True)\n",
    "for i in range(len(inputs_words)):\n",
    "    decode_text = tokenizer.decode(inputs_ids[i: i+1].tolist(), remove_bos=False, remove_eos=False, remove_pad=False, split=True)[0]\n",
    "    print(decode_text)\n",
    "    # print(input_padding_mask[i].reshape(1, -1))\n",
    "    #repeat_interleave作用：将input_padding_mask[i]重复inputs_ids.shape[-1]次，dim=0表示在第0维度上重复\n",
    "    self_attn_mask  = input_padding_mask[i].reshape(1, -1).repeat_interleave(inputs_ids.shape[-1], dim=0)\n",
    "    # print(input_mask[i].reshape(1, -1).repeat_interleave(inputs_ids.shape[-1], dim=0))\n",
    "    look_ahead_mask = generate_look_ahead_mask(inputs_ids.shape[-1])\n",
    "\n",
    "    fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n",
    "    axs[0].matshow(self_attn_mask)\n",
    "    axs[0].set_title(\"self_attn_mask\")\n",
    "    axs[0].set_yticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[0].set_ylabel(\"querys\")\n",
    "    axs[0].set_xticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[0].set_xlabel(\"keys\")\n",
    "    axs[1].matshow(look_ahead_mask)\n",
    "    axs[1].set_title(\"look_ahead_mask\")\n",
    "    axs[1].set_yticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[1].set_ylabel(\"querys\")\n",
    "    axs[1].set_xticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[1].set_xlabel(\"keys\")\n",
    "    plt.show()\n",
    "    print('-'*50)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84f2a6c9",
   "metadata": {},
   "source": [
    "## TransformerModel"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "59fa00df",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "from typing import Optional, List, Dict, Any, Tuple\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import math\n",
    "import numpy as np\n",
    "import time\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class TransformerOutput:\n",
    "    logits: torch.Tensor\n",
    "    encoder_attentions: Optional[List[torch.Tensor]] = None\n",
    "    decoder_attentions: Optional[List[torch.Tensor]] = None\n",
    "    cross_attentions: Optional[List[torch.Tensor]] = None\n",
    "    preds: Optional[torch.Tensor] = None\n",
    "\n",
    "\n",
    "class TransformerModel(nn.Module):\n",
    "    def __init__(self, config: Dict[str, Any]):\n",
    "        super().__init__()\n",
    "        self.d_model = config.get(\"d_model\", 512)\n",
    "        self.num_heads = config.get(\"num_heads\", 8)\n",
    "        self.pad_token_id = config.get(\"pad_token_id\", 0)\n",
    "        self.pad_idx = config.get(\"pad_idx\", 0)\n",
    "        self.vocab_size = config.get(\"vocab_size\")\n",
    "        self.share_embeddings = config.get(\"share_embeddings\", False)\n",
    "\n",
    "        # Embedding layers using TransformerEmbedding\n",
    "        self.src_embedding = TransformerEmbedding(config=config)\n",
    "\n",
    "        if self.share_embeddings:\n",
    "            self.tgt_embedding = self.src_embedding\n",
    "            # Share embedding weights with output projection layer\n",
    "            self.output_projection = lambda x: torch.matmul(\n",
    "                x, self.tgt_embedding.get_word_embedding_weights().T\n",
    "            )  # 输出层，共享参数，直接拿原有embedding矩阵的转置，节省内存\n",
    "        else:\n",
    "            self.tgt_embedding = TransformerEmbedding(config=config)\n",
    "            self.output_projection = nn.Linear(self.d_model, self.vocab_size)\n",
    "\n",
    "        # Encoder and Decoder\n",
    "        self.encoder = TransformerEncoder(config=config)\n",
    "\n",
    "        self.decoder = TransformerDecoder(config=config)\n",
    "\n",
    "        # Initialize parameters with Xavier uniform\n",
    "        self._init_parameters()\n",
    "\n",
    "    def _init_parameters(self):\n",
    "        for p in self.parameters():\n",
    "            if p.dim() > 1:\n",
    "                nn.init.xavier_uniform_(p)\n",
    "\n",
    "    def forward(\n",
    "        self,\n",
    "        src_inputs: torch.Tensor,\n",
    "        tgt_inputs: torch.Tensor,\n",
    "        src_padding_mask: Optional[torch.Tensor] = None,\n",
    "        tgt_padding_mask: Optional[torch.Tensor] = None,\n",
    "    ) -> TransformerOutput:\n",
    "        batch_size, src_seq_len = src_inputs.size()\n",
    "        bs, tgt_seq_len = tgt_inputs.size()\n",
    "\n",
    "        # Create masks if not provided，实际是一定会传的，shape从[batch_size,src_seq_len]变为[batch_size,1,1,src_seq_len]\n",
    "        src_padding_mask = src_padding_mask.unsqueeze(1).unsqueeze(2)\n",
    "\n",
    "        if tgt_padding_mask is None:\n",
    "            tgt_padding_mask = tgt_inputs == self.pad_token_id\n",
    "\n",
    "        # Create look-ahead mask for decoder\n",
    "        look_ahead_mask = generate_look_ahead_mask(tgt_seq_len).to(tgt_inputs.device)\n",
    "\n",
    "        print(f\"tgt_padding_mask.shape:{tgt_padding_mask.shape}\")\n",
    "        # 合并decoder_inputs的padding_mask和look_ahead_mask,shape变为[batch_size,tgt_seq_len,tgt_seq_len]\n",
    "        combined_mask = torch.max(\n",
    "            tgt_padding_mask.unsqueeze(1).repeat(1, tgt_seq_len, 1), look_ahead_mask\n",
    "        )\n",
    "        combined_mask = combined_mask.unsqueeze(\n",
    "            1\n",
    "        )  # 变为[batch_size, 1, tgt_seq_len, tgt_seq_len]\n",
    "        print(f\"combined_mask.shape:{combined_mask.shape}\")\n",
    "\n",
    "        # Embeddings\n",
    "        src_embedded = self.src_embedding(src_inputs)\n",
    "        tgt_embedded = self.tgt_embedding(tgt_inputs)\n",
    "\n",
    "        # 编码器模块计算\n",
    "        encoder_output = self.encoder(src_embedded, attention_mask=src_padding_mask)\n",
    "        print(f\"src_padding_mask.shape:{src_padding_mask.shape}\")\n",
    "        print(f\"combined_mask.shape:{combined_mask.shape}\")\n",
    "        # 解码器模块计算\n",
    "        decoder_output = self.decoder(\n",
    "            tgt_embedded,\n",
    "            encoder_output.last_hidden_states,\n",
    "            self_attn_mask=combined_mask,\n",
    "            cross_attn_mask=src_padding_mask,  # 交叉注意力mask\n",
    "        )\n",
    "\n",
    "        # 分类层\n",
    "        logits = self.output_projection(decoder_output.last_hidden_states)\n",
    "\n",
    "        return TransformerOutput(\n",
    "            logits=logits,\n",
    "            encoder_attentions=encoder_output.attn_scores,\n",
    "            decoder_attentions=decoder_output.self_attn_scores,\n",
    "            cross_attentions=decoder_output.cross_attn_scores,\n",
    "        )\n",
    "\n",
    "    def infer(self, encoder_inputs, encoder_inputs_mask=None):\n",
    "        if encoder_inputs_mask is None:  # 应对多个样本同时进行推理\n",
    "            encoder_inputs_mask = encoder_inputs.eq(self.pad_idx)\n",
    "        encoder_inputs_mask = encoder_inputs_mask.unsqueeze(1).unsqueeze(\n",
    "            2\n",
    "        )  # [batch_size, 1, 1, src_len],[1,src_len]相加时，会自动广播到[batch_size,1,src_len,src_len]\n",
    "        look_ahead_mask = self.generate_square_subsequent_mask(self.max_length)\n",
    "        look_ahead_mask = (\n",
    "            look_ahead_mask.unsqueeze(0).unsqueeze(0).to(encoder_inputs.device)\n",
    "        )  # [1, 1, trg_len, trg_len]\n",
    "\n",
    "        # 做embedding\n",
    "        encoder_inputs_embeds = self.src_embedding(encoder_inputs)\n",
    "        encoder_outputs = self.encoder(\n",
    "            encoder_inputs_embeds,\n",
    "            encoder_inputs_mask\n",
    "        )\n",
    "        # 解码器模块计算\n",
    "        decoder_inputs = torch.Tensor([self.bos_idx] * encoder_inputs.shape[0]).reshape(-1, 1).long().to(device=encoder_inputs.device)\n",
    "        for cur_len in tqdm(range(1, self.max_length + 1)):\n",
    "            # 做decoder的embedding\n",
    "            decoder_inputs_embeds = self.trg_embedding(decoder_inputs)\n",
    "            decoder_outputs = self.decoder(\n",
    "                decoder_inputs_embeds,\n",
    "                encoder_outputs.last_hidden_states,\n",
    "                look_ahead_mask[:, :, :cur_len, :cur_len],#decoder的自注意力mask\n",
    "                encoder_inputs_mask\n",
    "            )\n",
    "\n",
    "            logits = self.output_projection(decoder_outputs.last_hidden_states)  # (batch_size, trg_len, vocab_size)\n",
    "            next_token = logits.argmax(dim=-1)[:, -1:] #通过最大下标确定类别，[:, -1:]表示取最后一个结果\n",
    "            decoder_inputs = torch.cat([decoder_inputs, next_token], dim=-1) #预测输出拼接到输入中\n",
    "            #(decoder_inputs == self.eos_idx).sum(dim=-1)是判断样本中是否含有EOS标记\n",
    "            #all是每一个都为True，才会结束\n",
    "            if all((decoder_inputs == self.eos_idx).sum(dim=-1) > 0):\n",
    "                break\n",
    "        return TransformerOutput(\n",
    "            preds=decoder_inputs[:, 1:],\n",
    "            logits=logits,\n",
    "            encoder_attentions=encoder_outputs.attn_scores,\n",
    "            decoder_attentions=decoder_outputs.self_attn_scores,\n",
    "            cross_attentions=decoder_outputs.cross_attn_scores,\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "f15f3884",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[    1,     2, 10380,   125,     2,   337,   143,    10,     2,    57,\n",
      "             4,     3],\n",
      "        [    1,     2,   768,    10,     2,  5139,  3702,     3,     0,     0,\n",
      "             0,     0]])\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]])\n",
      "tensor([[   1,    2, 4852,  119,   10,  703, 1668,    2,    4,    3],\n",
      "        [   1,    2,  785,   16, 8504,   10,  271,  460,    3,    0]])\n",
      "tgt_padding_mask.shape:torch.Size([2, 10])\n",
      "combined_mask.shape:torch.Size([2, 1, 10, 10])\n",
      "scaled_attention_logits.shape:torch.Size([2, 8, 12, 12])\n",
      "mask.shapetorch.Size([2, 1, 1, 12])\n",
      "scaled_attention_logits.shape:torch.Size([2, 8, 12, 12])\n",
      "mask.shapetorch.Size([2, 1, 1, 12])\n",
      "scaled_attention_logits.shape:torch.Size([2, 8, 12, 12])\n",
      "mask.shapetorch.Size([2, 1, 1, 12])\n",
      "scaled_attention_logits.shape:torch.Size([2, 8, 12, 12])\n",
      "mask.shapetorch.Size([2, 1, 1, 12])\n",
      "scaled_attention_logits.shape:torch.Size([2, 8, 12, 12])\n",
      "mask.shapetorch.Size([2, 1, 1, 12])\n",
      "scaled_attention_logits.shape:torch.Size([2, 8, 12, 12])\n",
      "mask.shapetorch.Size([2, 1, 1, 12])\n",
      "src_padding_mask.shape:torch.Size([2, 1, 1, 12])\n",
      "combined_mask.shape:torch.Size([2, 1, 10, 10])\n",
      "scaled_attention_logits.shape:torch.Size([2, 8, 10, 10])\n",
      "mask.shapetorch.Size([2, 1, 10, 10])\n",
      "scaled_attention_logits.shape:torch.Size([2, 8, 10, 12])\n",
      "mask.shapetorch.Size([2, 1, 1, 12])\n",
      "scaled_attention_logits.shape:torch.Size([2, 8, 10, 10])\n",
      "mask.shapetorch.Size([2, 1, 10, 10])\n",
      "scaled_attention_logits.shape:torch.Size([2, 8, 10, 12])\n",
      "mask.shapetorch.Size([2, 1, 1, 12])\n",
      "Forward pass (training mode) successful!\n",
      "Output logits shape: torch.Size([2, 10, 18111])\n",
      "Encoder attention shape: torch.Size([2, 8, 12, 12])\n",
      "Decoder attention shape: torch.Size([2, 8, 10, 10])\n",
      "Cross attention shape: torch.Size([2, 8, 10, 12])\n"
     ]
    }
   ],
   "source": [
    "# Example to verify the forward computation of TransformerModel\n",
    "\n",
    "# Define sample input\n",
    "batch_size = 2\n",
    "d_model = 512\n",
    "\n",
    "inputs_words = [\"The quick brown fox jumps over the lazy dog .\", \"What does the fox say ?\"]\n",
    "\n",
    "#encode变为id\n",
    "src_sequence, src_padding_mask = tokenizer.encode([w.split() for w in inputs_words], return_mask=True)\n",
    "\n",
    "# Create two random examples similar to inputs_words\n",
    "target_examples = [\n",
    "    \"She walked through the beautiful garden yesterday .\",\n",
    "    \"The tall man opened the old wooden \"\n",
    "]\n",
    "\n",
    "\n",
    "tgt_sequence= tokenizer.encode([w.split() for w in target_examples], return_mask=False)\n",
    "\n",
    "\n",
    "print(src_sequence)\n",
    "print(src_padding_mask)\n",
    "print(tgt_sequence)\n",
    "\n",
    "# Create a sample model (use small dimensions for testing)\n",
    "config = {\n",
    "    'vocab_size': vocab_size,\n",
    "    'd_model': d_model,\n",
    "    'num_heads': 8,\n",
    "    'num_encoder_layers': 2,\n",
    "    'num_decoder_layers': 2,\n",
    "    'd_ff': 1024,\n",
    "    'dropout': 0.1,\n",
    "    'max_length': 100,\n",
    "    'pad_idx': 0\n",
    "}\n",
    "model = TransformerModel(config=config).to(device)\n",
    "\n",
    "# Test forward pass (training mode)\n",
    "output = model(\n",
    "    src_inputs=src_sequence,\n",
    "    tgt_inputs=tgt_sequence,\n",
    "    src_padding_mask=(src_sequence == 0),\n",
    "    tgt_padding_mask=(tgt_sequence == 0),\n",
    ")\n",
    "\n",
    "print(\"Forward pass (training mode) successful!\")\n",
    "print(f\"Output logits shape: {output.logits.shape}\")\n",
    "\n",
    "# Verify encoder attention shape\n",
    "if output.encoder_attentions:\n",
    "    print(f\"Encoder attention shape: {output.encoder_attentions[0].shape}\")\n",
    "\n",
    "# Verify decoder attention shape\n",
    "if output.decoder_attentions:\n",
    "    print(f\"Decoder attention shape: {output.decoder_attentions[0].shape}\")\n",
    "\n",
    "# Verify cross attention shape\n",
    "if output.cross_attentions:\n",
    "    print(f\"Cross attention shape: {output.cross_attentions[0].shape}\")\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
